Skip to content
Snippets Groups Projects
Commit 727269c0 authored by Lucie Bader's avatar Lucie Bader
Browse files

Récupération scripts Alissa

parent b8e717cb
No related branches found
No related tags found
1 merge request!8Test final
......@@ -64,7 +64,13 @@
"src/utils/stats.js"],
"css": ["src/context_menu/custom_context_menu.css"],
"run_at": "document_idle"
},
{
"matches": ["<all_urls>"],
"js": ["src/utils/stats.js"],
"run_at": "document_end"
}
],
"web_accessible_resources": [
......
......@@ -45,7 +45,7 @@ browser.storage.onChanged.addListener((changes, area) => {
if (!extensionActive) {
log("Token ajouté, activation automatique de l'extension.");
browser.storage.local.set({ extensionActive: true });
enableExtensionFeatures();
enableExtensionFeatures();
browser.runtime.sendMessage({
action: "updateUI",
extensionActive: true,
......@@ -71,90 +71,6 @@ async function refreshAllUI() {
browser.runtime.sendMessage({ action: "refreshUI" });
}
// ─────────────────────────────────────────────────────────────────────────────
// Initialisation du WebWorker
// ─────────────────────────────────────────────────────────────────────────────
let worker = null;
function initWorker() {
if (!worker) {
log("[Background] Initialisation du WebWorker...");
try {
worker = new Worker("src/workers/pyodide_worker.js");
worker.onmessage = (event) => {
log("[Background] Message reçu du WebWorker :", event.data);
};
worker.onerror = (error) => {
console.error("[Background] Erreur dans le WebWorker :", error);
};
log("[Background] WebWorker initialisé avec succès.");
} catch (error) {
console.error("[Background] Échec de l'initialisation du WebWorker :", error);
}
}
}
// Initialisation
initWorker();
worker.postMessage({ command: "pyodide-simplemma" });
// ─────────────────────────────────────────────────────────────────────────────
// Écoute des messages de la popup et transmission au WebWorker
// ─────────────────────────────────────────────────────────────────────────────
browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
log("[Background] Message reçu :", message);
if (!worker) {
initWorker();
}
if (message.command === "toggle-stats") {
log(`[Background] Statistiques ${message.isActive ? "activées" : "désactivées"}`);
await browser.storage.local.set({ isTrackingActive: message.isActive });
// Transmettre l'activation des statistiques au WebWorker
worker.postMessage({ command: "toggle-stats", isActive: message.isActive });
}
if (message.command === "pyodide-simplemma") {
log("[Background] Demande d'initialisation de Pyodide et Simplemma...");
worker.postMessage({ command: "pyodide-simplemma" });
}
return true;
});
// ─────────────────────────────────────────────────────────────────────────────
// Écouter les réponses du WebWorker
// ─────────────────────────────────────────────────────────────────────────────
worker.onmessage = (event) => {
log("[Background] Message du WebWorker :", event.data);
// Pyodide et Simplemma prêts : notifier tous les onglets
if (event.data.type === "pyodide-simplemma" && event.data.status === "success") {
browser.tabs.query({}).then((tabs) => {
tabs.forEach((tab) => {
if (!tab.url || (!tab.url.startsWith("https://") && (!tab.url.startsWith("https://")))) {
return;
}
if (tab.url.includes("sidebar.html")) {
return;
}
try {
let response = browser.tabs.sendMessage(tab.id, { command: "pyodide-simplemma-ready" })
if (response && typeof response.then === "function") {
response.then(() => {
}).catch((error) => {
log("[Background] Impossible d'envoyer un message à l'onglet ${tab.id} : ${error}");
});
}
} catch (error) {
log("[Background] Erreur lors de l'envoi d'un message à l'onglet ${tab.id} : ${error}");
}
});
})
}
}
// ─────────────────────────────────────────────────────────────────────────────
// Fonctions d'authentification & de redirection
// ─────────────────────────────────────────────────────────────────────────────
......@@ -191,21 +107,20 @@ async function actuallyOpenLoginPage() {
browser.runtime.sendMessage({ action: "authStatusChanged", isLoggedIn: false });
}
// Déconnecte l'utilisateur
async function disconnectFromLexicalDB() {
await browser.storage.local.remove("accessToken");
log("🔓 Token supprimé avec succès.");
await browser.storage.local.remove("lexiconColors");
// Désactivation automatique de l'extension
await browser.storage.local.set({ extensionActive: false });
disableExtensionFeatures();
browser.runtime.sendMessage({
action: "updateUI",
extensionActive: false,
isTrackingActive: false,
autoAdd: false
disableExtensionFeatures();
browser.runtime.sendMessage({
action: "updateUI",
extensionActive: false,
isTrackingActive: false,
autoAdd: false
});
setTimeout(async () => {
......@@ -213,7 +128,6 @@ async function disconnectFromLexicalDB() {
}, 500);
}
// Sauvegarde le token et ferme l'onglet de login si nécessaire
async function saveToken(token) {
log("✅ Sauvegarde du token :", token);
await browser.storage.local.set({ accessToken: token });
......@@ -253,7 +167,7 @@ async function saveToken(token) {
}
// ─────────────────────────────────────────────────────────────────────────────
// Gestion des messages reçus
// Gestion des messages reçus (depuis popup, etc.)
// ─────────────────────────────────────────────────────────────────────────────
browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
log("📩 Message reçu dans background.js :", message);
......@@ -414,65 +328,218 @@ function showInstructionPopup(details) {
}
// ─────────────────────────────────────────────────────────────────────────────
// Gestion de l'activation/désactivation de l'extension
// Initialisation du WebWorker
// ─────────────────────────────────────────────────────────────────────────────
// === 1. Initialisation de l'état de l'extension ===
async function initializeExtensionState() {
const { extensionActive } = await browser.storage.local.get("extensionActive");
let worker = null;
if (extensionActive === undefined) {
await browser.storage.local.set({ extensionActive: true }); // Activation par défaut
log("🔄 Initialisation : extension activée par défaut.");
enableExtensionFeatures();
} else {
log(`🔄 État actuel de l'extension : ${extensionActive ? "activée" : "désactivée"}`);
if (extensionActive) {
enableExtensionFeatures();
} else {
disableExtensionFeatures();
function initWorker() {
if (!worker) {
console.log("[Background] Initialisation du WebWorker...");
try {
worker = new Worker(browser.runtime.getURL("src/workers/pyodide_worker.js"));
// Centralisation de l'écoute des messages et erreurs du Worker
worker.addEventListener("message", handleWorkerMessage);
worker.addEventListener("error", handleWorkerError);
console.log("[Background] WebWorker initialisé avec succès.");
} catch (error) {
console.error("[Background] Échec de l'initialisation du WebWorker :", error);
}
}
}
// === 2. Gestion de l'activation/désactivation de l'extension ===
browser.runtime.onMessage.addListener(async (message) => {
if (message.action === "toggleExtension") {
log(`🔄 Changement d'état de l'extension : ${message.isActive ? "activée" : "désactivée"}`);
await browser.storage.local.set({ extensionActive: message.isActive });
function handleWorkerMessage(event) {
const data = event.data;
console.log("[Background] Message du WebWorker :", data);
if (!message.isActive) {
disableExtensionFeatures();
} else {
enableExtensionFeatures();
switch (data.type) {
case "pyodide-simplemma":
if (data.status === "success") {
console.log("[Background] Pyodide et Simplemma prêts. Mise à jour de l'état.");
browser.storage.local.set({ pyodideSimplemmaReady: true });
checkAndUpdateTracking();
}
break;
case "update-frequencies":
console.log("[Background] Mise à jour des fréquences :", data.frequencies);
notifyAllTabs({ command: "update-frequencies", frequencies: data.frequencies });
// Si un port stats est connecté, vous pouvez lui transmettre également :
// port.postMessage({ command: "update-frequencies", frequencies: data.frequencies });
browser.storage.local.set({ lemmaFrequencies: data.frequencies });
break;
case "threshold-exceeded":
console.log("[Background] Mots dépassant le seuil :", data.wordsAboveThreshold);
notifyAllTabs({ command: "threshold-exceeded", wordsAboveThreshold: data.wordsAboveThreshold });
break;
default:
console.warn("[Background] Message non traité du Worker :", data);
break;
}
}
function handleWorkerError(error) {
console.error("[Background] Erreur dans le WebWorker :", error);
}
// Initialisation du worker dès le démarrage
initWorker();
// ─────────────────────────────────────────────────────────────────────────────
// Écoute des messages de la popup et transmission au WebWorker
// ─────────────────────────────────────────────────────────────────────────────
browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
console.log("[Background] Message reçu :", message);
if (!worker) {
initWorker();
}
if (message.command === "toggle-stats") {
console.log(`[Background] Statistiques ${message.isActive ? "activées" : "désactivées"}`);
const { isActive } = message;
await browser.storage.local.set({ isTrackingActive: isActive });
if (!isActive) {
await browser.storage.local.set({ autoAdd: false });
}
browser.runtime.sendMessage({ action: "updateUI" });
checkAndUpdateTracking();
}
if (message.command === "pyodide-simplemma") {
console.log("[Background] Demande d'initialisation de Pyodide et Simplemma...");
worker.postMessage({ command: "pyodide-simplemma" });
}
return true;
});
// === 3. Fonction pour désactiver les fonctionnalités de l'extension ===
async function disableExtensionFeatures() {
log("Désactivation des fonctionnalités de l'extension.");
browser.runtime.sendMessage({
action: "updateUI",
autoAdd: false,
isTrackingActive: false,
areStatsActive: false
});
// Suppression d'éventuelles actions spécifiques à la sidebar
log("Les fonctionnalités de la sidebar ne sont plus utilisées.");
// ─────────────────────────────────────────────────────────────────────────────
// Chargement et sauvegarde des fréquences stockées
// ─────────────────────────────────────────────────────────────────────────────
async function loadStoredFrequencies() {
const { storedFrequencies } = await browser.storage.local.get("storedFrequencies");
return storedFrequencies || {};
}
// === 4. Fonction pour activer les fonctionnalités de l'extension ===
async function enableExtensionFeatures() {
log("Réactivation des fonctionnalités de l'extension.");
browser.runtime.sendMessage({
action: "updateUI",
autoAdd: true,
isTrackingActive: true,
areStatsActive: true
}).catch(err => {
console.warn("Aucun récepteur pour le message updateUI :", err);
let storedFrequencies = {};
loadStoredFrequencies().then(frequencies => {
storedFrequencies = frequencies;
console.log("[Background] Fréquences initialisées :", storedFrequencies);
});
// ─────────────────────────────────────────────────────────────────────────────
// Statistiques : Vérification et activation/désactivation du tracking
// ─────────────────────────────────────────────────────────────────────────────
async function checkAndUpdateTracking() {
const { isTrackingActive, pyodideSimplemmaReady } = await browser.storage.local.get(["isTrackingActive", "pyodideSimplemmaReady"]);
if (isTrackingActive && pyodideSimplemmaReady) {
console.log("[Background] Activation du tracking.");
notifyAllTabs({ command: "activate-stats" });
} else {
console.log("[Background] Désactivation du tracking.");
notifyAllTabs({ command: "deactivate-stats" });
}
}
// ─────────────────────────────────────────────────────────────────────────────
// Statistiques : Gestion des onglets
// ─────────────────────────────────────────────────────────────────────────────
async function notifyAllTabs(message) {
browser.tabs.query({}).then((tabs) => {
tabs.forEach((tab) => {
browser.tabs.sendMessage(tab.id, message)
.catch((error) => console.warn(`[Background] Impossible d'envoyer un message à l'onglet ${tab.id} : ${error}`));
});
});
}
initializeExtensionState();
// ─────────────────────────────────────────────────────────────────────────────
// Statistiques : Écoute des modifications du stockage et mise à jour du tracking
// ─────────────────────────────────────────────────────────────────────────────
browser.storage.onChanged.addListener(async (changes, area) => {
if (area === "local" && (changes.isTrackingActive || changes.pyodideSimplemmaReady)) {
checkAndUpdateTracking();
}
if (area === "local" && (changes.accessToken || changes.threshold || changes.trackedLanguages || changes.autoAdd)) {
console.log("[Background] Mise à jour des préférences détectée.");
const { accessToken, trackedLanguages, threshold, autoAdd } = await browser.storage.local.get([
"accessToken", "trackedLanguages", "threshold", "autoAdd"
]);
const isAuthenticated = !!accessToken;
worker.postMessage({
command: "update-preferences",
isAuthenticated,
trackedLanguages: trackedLanguages || [],
threshold: threshold || 10,
autoAdd: autoAdd || false
});
}
});
// ─────────────────────────────────────────────────────────────────────────────
// Écoute de l'inscription de stats.js
// ─────────────────────────────────────────────────────────────────────────────
browser.runtime.onMessage.addListener(async (message, sender) => {
if (message.command === "register-stats-script") {
console.log("[Background] stats.js s'est enregistré.");
const { isTrackingActive, pyodideSimplemmaReady } = await browser.storage.local.get(["isTrackingActive", "pyodideSimplemmaReady"]);
if (isTrackingActive && pyodideSimplemmaReady) {
browser.tabs.sendMessage(sender.tab.id, { command: "activate-stats" })
.catch((error) => console.warn(`[Background] Impossible d'envoyer activate-stats à ${sender.tab.id} : ${error}`));
}
}
});
// ─────────────────────────────────────────────────────────────────────────────
// Connexion entre stats.js et le Worker via un port dédié
// ─────────────────────────────────────────────────────────────────────────────
browser.runtime.onConnect.addListener((port) => {
console.log("[Background] Connexion établie :", port.name);
if (port.name === "stats-worker-port") {
// Redirige les messages de stats.js vers le Worker
port.onMessage.addListener((message) => {
console.log("[Background] Message reçu de stats.js :", message);
worker.postMessage(message);
});
// Transmettre les réponses du Worker à stats.js via le port
worker.addEventListener("message", (event) => {
const data = event.data;
if (data.type === "update-frequencies") {
port.postMessage({ command: "update-frequencies", frequencies: data.frequencies });
}
if (data.type === "threshold-exceeded") {
port.postMessage({ command: "threshold-exceeded", wordsAboveThreshold: data.wordsAboveThreshold });
}
});
}
});
// ─────────────────────────────────────────────────────────────────────────────
// Stoplists : Chargement et envoi au Worker
// ─────────────────────────────────────────────────────────────────────────────
let stoplistFr = [];
function loadStoplist() {
fetch(browser.runtime.getURL("stoplist_fr.txt"))
.then(response => response.text())
.then(text => {
stoplistFr = text.split("\n").map(word => word.trim());
console.log("[Background] Stoplist chargée :", stoplistFr);
sendStoplistToWorker();
})
.catch(error => console.error("[Background] Erreur lors du chargement de la stoplist :", error));
}
function sendStoplistToWorker() {
console.log("[Background] Envoi de la stoplist au Worker...");
worker.postMessage({ command: "update-stoplist", stoplist: stoplistFr });
}
browser.runtime.onStartup.addListener(loadStoplist);
browser.runtime.onInstalled.addListener(loadStoplist);
// ─────────────────────────────────────────────────────────────────────────────
// (Code commenté concernant l'activation/désactivation de l'analyse)
// ─────────────────────────────────────────────────────────────────────────────
// async function initializeExtensionState() { ... }
// initializeExtensionState();
@font-face {
font-family: 'Luciole';
src: url('../fonts/Luciole-Regular/Luciole-Regular.woff2') format('woff2'),
url('../fonts/Luciole-Regular/Luciole-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
}
/* === Conteneur principal du menu contextuel === */
#whiteBox {
......
......@@ -8,8 +8,8 @@
<style>
@font-face {
font-family: 'Luciole';
src: url('chemin/vers/Luciole-Regular.woff2') format('woff2'),
url('chemin/vers/Luciole-Regular.woff') format('woff');
src: url('../fonts/Luciole-Regular/Luciole-Regular.woff2') format('woff2'),
url('../fonts/Luciole-Regular/Luciole-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
}
......
log("✅ popup.js chargé avec succès !");
// === 1. Mise à jour du bouton Connexion/Déconnexion ===
async function updateConnectionButton() {
// ==========================
// Fonctions utilitaires
// ==========================
async function getAccessToken() {
const { accessToken } = await browser.storage.local.get("accessToken");
return accessToken;
}
// ==========================
// Gestion de la connexion
// ==========================
async function updateConnectionButton() {
const accessToken = await getAccessToken();
const button = document.getElementById("auth-button");
if (button) {
if (accessToken) {
button.textContent = "Se déconnecter";
button.title = "En vous déconnectant, vous perdrez l'accès à vos lexiques personnels, ainsi que les fonctionnalités d'ajout automatique et de statistiques d'utilisation.";
} else {
button.textContent = "Se connecter";
button.title = "En vous connectant, vous pourrez accéder à vos lexiques personnels, ainsi qu'aux fonctionnalités d'ajout automatique et de statistiques d'utilisation.";
}
button.onclick = async () => {
await browser.runtime.sendMessage({ action: "toggleAuth" });
};
} else {
if (!button) {
console.error("❌ Le bouton de connexion n'a pas été trouvé.");
return;
}
if (accessToken) {
button.textContent = "Se déconnecter";
button.title = "En vous déconnectant, vous perdrez l'accès à vos lexiques personnels, ainsi que les fonctionnalités d'ajout automatique et de statistiques d'utilisation.";
} else {
button.textContent = "Se connecter";
button.title = "En vous connectant, vous pourrez accéder à vos lexiques personnels, ainsi qu'aux fonctionnalités d'ajout automatique et de statistiques d'utilisation.";
}
button.onclick = async () => {
await browser.runtime.sendMessage({ action: "toggleAuth" });
};
}
// === 2. Mise à jour dynamique de la sélection des langues ===
// ==========================
// Gestion de la sélection des langues
// ==========================
async function updateLanguageSelection() {
const languageSelection = document.getElementById("language-selection");
languageSelection.innerHTML = "<p id='loading-languages' style='color: gray;'>Chargement...</p>";
const { accessToken } = await browser.storage.local.get("accessToken");
const accessToken = await getAccessToken();
if (!accessToken) {
languageSelection.innerHTML = "<p style='color: red;'>Veuillez vous connecter.</p>";
return;
}
// Utilisation de la fonction de récupération de lexiques
// Choisissez ici entre getLexicons et getUserLexicons en fonction de votre implémentation
const lexicons = await getLexicons(accessToken);
const userLanguages = [...new Set(lexicons.map(lex => lex.language))];
// Récupérer les langues suivies depuis le stockage
const { trackedLanguages } = await browser.storage.local.get("trackedLanguages") || { trackedLanguages: [] };
languageSelection.innerHTML = "";
languageSelection.innerHTML = "";
if (userLanguages.length === 0) {
languageSelection.innerHTML = "<p style='color: red;'>Aucun lexique personnel trouvé.</p>";
return;
......@@ -50,64 +65,62 @@ async function updateLanguageSelection() {
langButton.classList.add("lang-option");
langButton.textContent = lang.toUpperCase();
langButton.dataset.value = lang;
if (trackedLanguages && trackedLanguages.includes(lang)) {
langButton.classList.add("selected");
}
langButton.addEventListener("click", () => {
langButton.classList.toggle("selected");
});
languageSelection.appendChild(langButton);
});
log("✅ Sélection des langues mise à jour avec :", userLanguages);
}
// === 3. Mise à jour des options utilisateur et des statistiques ===
// ==========================
// Gestion des options et des statistiques
// ==========================
async function updateOptionsUI() {
const { accessToken, extensionActive } = await browser.storage.local.get(["accessToken", "extensionActive"]);
const accessToken = await getAccessToken();
const isLoggedIn = !!accessToken;
const toggleStatsBtn = document.getElementById("toggleStatsBtn");
const statsOptions = document.getElementById("stats-options");
const autoAddContainer = document.getElementById("auto-add")?.parentElement;
const autoAddCheckbox = document.getElementById("auto-add");
const autoAddOptions = document.getElementById("auto-add-options");
const thresholdInput = document.getElementById("threshold");
const saveOptionsBtn = document.getElementById("save-options");
const toggleStatsBtn = document.getElementById("toggleStatsBtn");
const openStats = document.getElementById("open-stats");
// Affichage de l'option "Ajout automatique" selon la connexion
if (autoAddContainer) {
autoAddContainer.style.display = isLoggedIn ? "block" : "none";
}
// Chargement des préférences
const { isTrackingActive, autoAdd, threshold } = await browser.storage.local.get([
"isTrackingActive",
"autoAdd",
"threshold"
]) || { isTrackingActive: false };
// Gestion du bouton de statistiques
if (toggleStatsBtn) {
if (!isLoggedIn) {
openStats.style.display = "none";
} else {
openStats.style.display = "block";
}
if (!accessToken) {
toggleStatsBtn.textContent = "Activer les statistiques";
toggleStatsBtn.style.opacity = "0.5";
toggleStatsBtn.title = "Connectez-vous pour activer les statistiques";
if (openStats) openStats.style.display = "none";
} else {
toggleStatsBtn.style.opacity = "1";
toggleStatsBtn.textContent = isTrackingActive ? "Désactiver les statistiques" : "Activer les statistiques";
toggleStatsBtn.title = "";
if (openStats) openStats.style.display = "block";
}
toggleStatsBtn.textContent = isTrackingActive ? "Désactiver les statistiques" : "Activer les statistiques";
}
// Affichage des options statistiques
if (statsOptions) {
statsOptions.classList.toggle("hidden", !extensionActive || !isTrackingActive);
statsOptions.classList.toggle("hidden", !isTrackingActive);
}
if (isLoggedIn) {
......@@ -136,96 +149,38 @@ async function updateOptionsUI() {
}
}
// === 4. Gestion des événements pour options/statistiques ===
document.getElementById("toggleStatsBtn").addEventListener("click", async () => {
const { accessToken } = await browser.storage.local.get("accessToken");
if (!accessToken) {
return;
}
const current = await browser.storage.local.get("isTrackingActive");
const newState = !(current.isTrackingActive);
await browser.storage.local.set({ isTrackingActive: newState });
if (!newState) {
await browser.storage.local.set({ autoAdd: false });
document.getElementById("auto-add").checked = false;
document.getElementById("auto-add-options").classList.add("hidden");
document.getElementById("save-options").classList.add("hidden");
}
document.getElementById("toggleStatsBtn").textContent = newState ? "Désactiver les statistiques" : "Activer les statistiques";
document.getElementById("stats-options").classList.toggle("hidden", !newState);
browser.runtime.sendMessage({ command: "toggle-stats", isActive: newState });
if (newState) {
log("[Popup] Demande d'initialisation de Pyodide...");
browser.runtime.sendMessage({ command: "init-pyodide" });
}
});
document.getElementById("auto-add").addEventListener("change", async () => {
const isChecked = document.getElementById("auto-add").checked;
document.getElementById("auto-add-options").classList.toggle("hidden", !isChecked);
document.getElementById("save-options").classList.toggle("hidden", !isChecked);
if (!isChecked) {
await browser.storage.local.set({ autoAdd: false });
}
});
document.getElementById("save-options").addEventListener("click", async () => {
const autoAdd = document.getElementById("auto-add").checked;
const threshold = parseInt(document.getElementById("threshold").value, 10);
const selectedLanguages = Array.from(document.querySelectorAll("#language-selection .lang-option.selected"))
.map(option => option.dataset.value);
await browser.storage.local.set({
autoAdd,
threshold,
trackedLanguages: selectedLanguages
});
log("Options sauvegardées :", { autoAdd, threshold, trackedLanguages: selectedLanguages });
});
document.getElementById("open-stats").addEventListener("click", function() {
window.open("stats.html", "_blank");
});
// === 5. Gestion de l'activation/désactivation de l'analyse ===
// ==========================
// Gestion de l'activation/désactivation de l'extension
// ==========================
async function updateExtensionToggleButton() {
const { accessToken, extensionActive } = await browser.storage.local.get(["accessToken", "extensionActive"]);
const toggleButton = document.getElementById("toggleExtensionBtn");
if (toggleButton) {
if (!accessToken) {
toggleButton.textContent = "Activer l'analyse";
toggleButton.style.opacity = "0.5";
toggleButton.title = "Connectez-vous pour activer l'analyse";
} else {
toggleButton.style.opacity = "1";
toggleButton.textContent = extensionActive ? "Désactiver l'analyse" : "Activer l'analyse";
toggleButton.title = "";
}
if (!toggleButton) {
console.error("❌ Le bouton d'activation de l'analyse n'a pas été trouvé.");
return;
}
if (!toggleButton.dataset.listenerAdded) {
toggleButton.addEventListener("click", handleToggleExtension);
toggleButton.dataset.listenerAdded = "true";
}
if (!accessToken) {
toggleButton.textContent = "Activer l'analyse";
toggleButton.style.opacity = "0.5";
toggleButton.title = "Connectez-vous pour activer l'analyse";
} else {
console.error("Le bouton d'activation de l'anlyse n'a pas été trouvé.");
toggleButton.style.opacity = "1";
toggleButton.textContent = extensionActive ? "Désactiver l'analyse" : "Activer l'analyse";
toggleButton.title = "";
}
// Ajout d'un écouteur unique
if (!toggleButton.dataset.listenerAdded) {
toggleButton.addEventListener("click", handleToggleExtension);
toggleButton.dataset.listenerAdded = "true";
}
}
// Gestion du clic sur le bouton d'activation/désactivation
function handleToggleExtension(event) {
browser.storage.local.get("accessToken").then(({ accessToken }) => {
if (!accessToken) {
return;
}
function handleToggleExtension() {
getAccessToken().then(accessToken => {
if (!accessToken) return;
proceedToggleExtension();
});
}
......@@ -236,8 +191,8 @@ async function proceedToggleExtension() {
isTrackingActive: false
});
const newState = !extensionActive;
await browser.storage.local.set({ extensionActive: newState });
const toggleButton = document.getElementById("toggleExtensionBtn");
if (toggleButton) {
toggleButton.textContent = newState ? "Désactiver l'analyse" : "Activer l'analyse";
......@@ -247,52 +202,94 @@ async function proceedToggleExtension() {
if (!newState) {
await browser.storage.local.set({ isTrackingActive: false });
if (isTrackingActive) {
window.open("stats.html", "_blank");
}
// Si les statistiques étaient activées, ouvrir la page stats
if (isTrackingActive) window.open("stats.html", "_blank");
browser.runtime.sendMessage({ action: "closeSidebarBlocks" });
}
await updateOptionsUI();
}
// === 6. Mise à jour de l'UI du popup en fonction d'un message ===
async function updatePopupUI(message) {
log("🔄 Mise à jour du popup avec :", message);
// ==========================
// Écouteurs d'événements
// ==========================
document.addEventListener("DOMContentLoaded", async () => {
await updateConnectionButton();
await updateOptionsUI();
await updateLanguageSelection();
await updateExtensionToggleButton();
});
const statsOptions = document.getElementById("stats-options");
const autoAddCheckbox = document.getElementById("auto-add");
const autoAddOptions = document.getElementById("auto-add-options");
// Gestion des statistiques et options
document.getElementById("toggleStatsBtn")?.addEventListener("click", async () => {
const accessToken = await getAccessToken();
if (!accessToken) return;
if (message.isTrackingActive !== undefined) {
if (statsOptions) {
statsOptions.classList.toggle("hidden", !message.isTrackingActive);
} else {
console.warn("⚠️ Élément #stats-options introuvable.");
}
const current = await browser.storage.local.get("isTrackingActive");
const newState = !current.isTrackingActive;
await browser.storage.local.set({ isTrackingActive: newState });
// Si désactivation, désactiver aussi l'ajout automatique
if (!newState) {
await browser.storage.local.set({ autoAdd: false });
document.getElementById("auto-add").checked = false;
document.getElementById("auto-add-options").classList.add("hidden");
document.getElementById("save-options").classList.add("hidden");
}
if (message.autoAdd !== undefined) {
if (autoAddCheckbox) {
autoAddCheckbox.checked = message.autoAdd;
} else {
console.warn("⚠️ Élément #auto-add introuvable.");
}
document.getElementById("toggleStatsBtn").textContent = newState ? "Désactiver les statistiques" : "Activer les statistiques";
document.getElementById("stats-options").classList.toggle("hidden", !newState);
if (autoAddOptions) {
autoAddOptions.classList.toggle("hidden", !message.autoAdd);
} else {
console.warn("⚠️ Élément #auto-add-options introuvable.");
}
// Envoi du message au background pour le changement de tracking
browser.runtime.sendMessage({ command: "toggle-stats", isActive: newState });
if (newState) {
log("[Popup] Demande d'initialisation de Pyodide et Simplemma");
// Garder la commande de file 2 pour l'initialisation de Pyodide/Simplemma
browser.runtime.sendMessage({ command: "pyodide-simplemma" });
}
});
const toggleButton = document.getElementById("toggleExtensionBtn");
if (toggleButton && message.extensionActive !== undefined) {
toggleButton.textContent = message.extensionActive ? "Désactiver l'analyse" : "Activer l'analyse";
// Gestion du changement de l'option d'ajout automatique
document.getElementById("auto-add")?.addEventListener("change", async () => {
const isChecked = document.getElementById("auto-add").checked;
document.getElementById("auto-add-options").classList.toggle("hidden", !isChecked);
document.getElementById("save-options").classList.toggle("hidden", !isChecked);
if (!isChecked) {
await browser.storage.local.set({ autoAdd: false });
}
}
});
// === 7. Écoute des messages depuis le background ===
// Sauvegarde des options utilisateur
document.getElementById("save-options")?.addEventListener("click", async () => {
const autoAdd = document.getElementById("auto-add").checked;
const threshold = parseInt(document.getElementById("threshold").value, 10);
const selectedLanguages = Array.from(document.querySelectorAll("#language-selection .lang-option.selected"))
.map(option => option.dataset.value);
// Vérification : si auto-add est activé, au moins une langue doit être sélectionnée
const errorMessage = document.getElementById("error-message");
if (autoAdd && selectedLanguages.length === 0) {
errorMessage?.classList.remove("hidden");
return;
}
errorMessage?.classList.add("hidden");
await browser.storage.local.set({
autoAdd,
threshold,
trackedLanguages: selectedLanguages
});
log("Options sauvegardées :", { autoAdd, threshold, trackedLanguages: selectedLanguages });
});
// Bouton pour ouvrir la page des statistiques
document.getElementById("open-stats")?.addEventListener("click", () => {
window.open("stats.html", "_blank");
});
// ==========================
// Réception des messages du background
// ==========================
browser.runtime.onMessage.addListener(async (message) => {
log("📩 Message reçu dans popup.js :", message);
......@@ -301,12 +298,14 @@ browser.runtime.onMessage.addListener(async (message) => {
await updateConnectionButton();
await updateOptionsUI();
await updateLanguageSelection();
updatePopupUI(message);
// Possibilité de mettre à jour d'autres éléments via le message
} else if (message.action === "showNotification") {
// Logique pour afficher une notification si nécessaire
// Gestion des notifications si besoin
showNotification(message.text);
}
});
// Actualisation de l'UI en cas de changement dans le stockage local
browser.storage.onChanged.addListener((changes, area) => {
if (area === "local" && changes.accessToken) {
updateConnectionButton();
......@@ -314,7 +313,9 @@ browser.storage.onChanged.addListener((changes, area) => {
}
});
// === 8. Notification d'activation/désactivation (optionnelle) ===
// ==========================
// Gestion de l'affichage de notifications (optionnelle)
// ==========================
function showNotification(message) {
const notificationBox = document.getElementById("extension-notification");
const notificationText = document.getElementById("notification-text");
......@@ -323,7 +324,6 @@ function showNotification(message) {
if (notificationBox && notificationText && closeButton) {
notificationText.textContent = message;
notificationBox.classList.remove("hidden");
closeButton.addEventListener("click", () => {
notificationBox.classList.add("hidden");
}, { once: true });
......@@ -331,12 +331,3 @@ function showNotification(message) {
console.error("❌ Impossible d'afficher la notification : élément manquant.");
}
}
// === 9. Initialisation lors du DOMContentLoaded ===
document.addEventListener("DOMContentLoaded", async () => {
await updateConnectionButton();
await updateOptionsUI();
await updateLanguageSelection();
await updateExtensionToggleButton();
});
......@@ -9,8 +9,14 @@
<script src="../utils/api.js" defer></script>
<script src="../utils/definitions.js" defer></script>
<script src="sidebar.js" defer></script>
<style>
@font-face {
font-family: 'Luciole';
src: url('../fonts/Luciole-Regular/Luciole-Regular.woff2') format('woff2'),
url('../fonts/Luciole-Regular/Luciole-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
}
/* Style global */
body {
font-family: Luciole;
......
......@@ -558,6 +558,10 @@ browser.runtime.onMessage.addListener(async (message) => {
case "pyodide-simplemma-ready":
return;
case "saveToken":
authToken = message.token;
break;
case "closeSidebarBlocks":
closeBlock("menuContent");
closeBlock("etatContent");
......
log("✅ api.js chargé correctement");
window.authToken = null;
// ─────────────────────────────────────────────────────────────────────────────
// ▌ Sélection de texte sur la page
// ─────────────────────────────────────────────────────────────────────────────
......@@ -218,5 +219,5 @@ window.getLexiconEntries = getLexiconEntries;
window.getAllLexiconWords = getAllLexiconWords;
window.getWiktionaryDefinition = getWiktionaryDefinition;
window.AddWord = AddWord;
window.getLexiconsColors = getLexiconsColors;
......@@ -125,46 +125,149 @@ async function fetchLexiconDefinitions(word) {
* 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) {
// async function fetchWiktionaryDefinition(word) {
// try {
// 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();
// 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.";
// 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."
// }
// ];
// }
// }
async function wikiApiResponse(word) {
const result = await browser.storage.local.get("accessToken");
authToken = result.accessToken;
// 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`;
try {
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);
const response = await fetch(wiktionaryApiUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${authToken}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Erreur API Wiktionnaire: ${response.statusText}`);
throw new Error(`Erreur lors de la récupération de la définition depuis le Wiktionnaire : ${response.statusText}`);
}
const data = await response.json();
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.";
const data = await response.json();
console.log(`Résultats du Wiktionnaire pour le mot "${word}" :`, data);
return data;
} catch (error) {
console.error('Erreur lors de la récupération de la définition depuis le Wiktionnaire :', error);
throw error;
}
}
log("🌍 Définition Wiktionnaire extraite :", definitionText);
function formatDefinitionData(apiResponse) {
let formattedData = {
word: apiResponse[0]?.id.split("-").slice(2).join("-") || "",
pronunciations: [],
definitions: [],
examples: [],
translations: {},
};
apiResponse.forEach(entry => {
const wordData = entry[entry.id.split(".").slice(-1)[0]]; // Accéder aux données via la clé dynamique
// Ajout des prononciations
if (wordData.pronunciations) {
formattedData.pronunciations.push(...wordData.pronunciations);
}
return [
{
source: "Wiktionnaire",
text: definitionText
// Ajout des définitions
if (wordData.senses) {
for (let senseKey in wordData.senses) {
let sense = wordData.senses[senseKey];
if (sense.Definitions) {
formattedData.definitions.push(...sense.Definitions.map(d => d.definition));
}
if (sense.Examples) {
formattedData.examples.push(...sense.Examples.map(e => e.example));
}
}
}
];
} catch (error) {
console.error("❌ Erreur Wiktionnaire :", error);
return [
{
source: "Wiktionnaire",
text: "⚠️ Erreur lors de la récupération sur le Wiktionnaire."
// Ajout des traductions
if (entry.translations) {
entry.translations.forEach(translation => {
if (!formattedData.translations[translation.lang_name]) {
formattedData.translations[translation.lang_name] = [];
}
formattedData.translations[translation.lang_name].push(translation.sense);
});
}
];
});
return formattedData;
}
async function fetchWiktionaryDefinition(word) {
try {
console.log(` Recherche de la définition pour : ${word}`);
// Récupération des données depuis l'API
const apiResponse = await wikiApiResponse(word);
console.log("Réponse brute de l'API :", apiResponse);
if (!Array.isArray(apiResponse) || apiResponse.length === 0) {
console.warn(`Aucune définition trouvée pour "${word}"`);
return []; // Retourne un tableau vide si aucune définition
}
// Formatage des données
const formattedData = formatDefinitionData(apiResponse);
console.log("Données formatées :", formattedData);
return [
{
source: "Wiktionnaire",
text: formattedData.definitions.length > 0 ? formattedData.definitions.join(" | ") : "⚠️ Aucune définition disponible."
}
];
} catch (error) {
console.error("Erreur lors de la récupération de la définition :", error);
return [{ source: "Wiktionnaire", text: "Erreur lors de la récupération sur le Wiktionnaire." }];
}
}
// ─────────────────────────────────────────────────────────────────────────────
// ▌ Affichage des définitions dans la barre latérale
// ─────────────────────────────────────────────────────────────────────────────
......
log("Script stats.js chargé !");
(function () {
if (window.hasRun) {
return;
}
window.hasRun = true;
let workerPort = null; // Port unique vers le WebWorker
// ─────────────────────────────────────────────────────────────────────────────
// Connexion/Transmission des données avec le WebWorker
// ─────────────────────────────────────────────────────────────────────────────
//Connexion au port
function connectToWorker() {
if (!workerPort) {
// console.log("[Stats] Connexion au WebWorker...");
workerPort = browser.runtime.connect({ name: "stats-worker-port" });
workerPort.onMessage.addListener((message) => {
console.log("[Stats] Message reçu du Worker :", message);
if (message.command === "update-frequencies") {
console.log("[Stats] Fréquences mises à jour :", message.frequencies);
}
if (message.command === "threshold-exceeded") {
console.log("[Stats] Mots dépassant le seuil :", message.wordsAboveThreshold);
let alertMessage = "Nouveaux mots dépassant le seuil :\n";
for (const [lang, words] of Object.entries(message.wordsAboveThreshold)) {
alertMessage += `\n🔹 ${lang.toUpperCase()} : ${words.join(", ")}`;
}
alert(alertMessage);
}
});
workerPort.onDisconnect.addListener(() => {
// console.log("[Stats] Déconnexion du WebWorker.");
workerPort = null;
});
}
}
//Fonction pour envoyer le texte directement au Worker
function sendTextToWorker(text) {
if (!workerPort) {
connectToWorker();
}
if (workerPort) {
console.log("[Stats] Envoi du texte au Worker :", text);
workerPort.postMessage({ command: "process-text", text: text });
}
}
// ─────────────────────────────────────────────────────────────────────────────
// Gestion des messages envoyés depuis le background
// ─────────────────────────────────────────────────────────────────────────────
browser.runtime.onMessage.addListener((message) => {
console.log("[Stats] Message reçu :", message);
if (message.command === "activate-stats") {
startTracking();
}
if (message.command === "deactivate-stats") {
stopTracking();
}
});
let isTrackingActive = false;
//Enregistrement manuel auprès du background pour écouter activate-stats
browser.runtime.sendMessage({ command: "register-stats-script" });
// ─────────────────────────────────────────────────────────────────────────────
// Extraction du texte sur les pages
// ─────────────────────────────────────────────────────────────────────────────
let scrollListenerAttached = false;
const seenContent = new Set();
let simplemmaReady = false;
const READING_SPEED_MS_PER_WORD = 250; // Seuil de lecture
// Stockage des chronos et des éléments lus
const readingTimers = new Map();
const readContent = new Set();
let userIsActive = false; // Indique si l'utilisateur est actif sur la page
// Détecte l'activité utilisateur pour éviter les fausses lectures
document.addEventListener("mousemove", () => userIsActive = true);
document.addEventListener("keydown", () => userIsActive = true);
setInterval(() => userIsActive = false, 60000); // Reset toutes les minutes
// Arrête tous les chronomètres lorsque l'utilisateur change d'onglet
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
// console.log("[Stats] Changement d'onglet détecté");
resetAllTimers();
}
});
/**
* Fonction pour extraire le texte visible et l'envoyer au background script.
* Fonction pour extraire le texte visible
*/
function trackVisibleContent() {
// pas de tracking tant que Pyodide n’est pas prêt
if (!simplemmaReady) {
log("[Stats] Pyodide n'est pas encore prêt, attente...");
return;
}
let selectors = "p, h1, h2, h3, h4, h5, h6, ul, ol, li, table, tr, td, th, blockquote, span, b";
let selectors = "p, h1, h2, h3, h4, h5, h6, ul, ol, li, table, tr, td, th, blockquote";
// Sélecteurs spécifiques à exclure sur Wikipedia et d'autres sites
let excludeSelectors = [
"#p-lang", // Liste des langues sur Wikipedia
"#footer", // Pied de page
".navbox", // Boîtes de navigation
".infobox", // Infobox
".sidebar", // Sidebars en général
"script", // Scripts JS
"style" // Styles CSS
"#p-lang", "#footer", ".navbox", ".infobox", ".sidebar", "script", "style", ".interlanguage-link"
];
document.querySelectorAll(selectors).forEach((element) => {
if (excludeSelectors.some(sel => element.closest(sel))) {
return; // On ignore cet élément
return; // Ignore les éléments exclus
}
const rect = element.getBoundingClientRect();
if (rect.top >= 0 && rect.bottom <= window.innerHeight) {
let text = element.innerText
text = cleanText(text); // nettoyage du texte avec cleanText
if (text.length > 10 && !seenContent.has(text)) {
seenContent.add(text);
log("[Stats] Envoi du texte filtré au background.js :", text);
const isVisible = rect.top >= 0 && rect.bottom <= window.innerHeight;
let text = cleanText(element.innerText);
let wordCount = text.split(/\s+/).length;
let minReadTime = wordCount * READING_SPEED_MS_PER_WORD;
if (text.length < 3) return; // Ignore les petits éléments
browser.runtime.sendMessage({ command: "process-text", text: text });
if (isVisible) {
if (!readContent.has(text)) {
startReadingTimer(element, text, minReadTime);
}
highlightElement(element, true);
} else {
stopReadingTimer(element, text);
highlightElement(element, false);
}
});
}
/**
* Ajoute une bordure rouge autour de l'élément
*/
function highlightElement(element, shouldHighlight) {
if (shouldHighlight) {
element.style.outline = "3px solid red";
} else {
element.style.outline = "";
removeReadingIndicator(element);
}
}
/**
* Démarre un chronomètre pour vérifier si l'élément est lu
*/
function startReadingTimer(element, text, minReadTime) {
if (!readingTimers.has(element)) {
let elapsedTime = 0;
let counter = document.createElement("div");
counter.classList.add("reading-counter");
counter.style.position = "absolute";
counter.style.background = "black";
counter.style.color = "white";
counter.style.padding = "4px 6px";
counter.style.borderRadius = "5px";
counter.style.fontSize = "12px";
counter.style.zIndex = "9999";
document.body.appendChild(counter);
let interval = setInterval(() => {
elapsedTime += 1000;
// Vérifie si l'utilisateur est actif et si le temps minimum est atteint
if (userIsActive && elapsedTime >= minReadTime) {
console.log(`[Stats] Élément lu : ${text}`);
readContent.add(text);
sendTextToWorker(text);
stopReadingTimer(element, text);
}
// Mise à jour de la position du compteur
let rect = element.getBoundingClientRect();
counter.style.top = `${rect.top + window.scrollY - 20}px`;
counter.style.left = `${rect.left + window.scrollX + rect.width + 10}px`;
counter.innerText = `⏳ ${Math.floor(elapsedTime / 1000)}s`;
}, 1000);
readingTimers.set(element, { interval, counter, elapsedTime });
}
}
/**
* Arrête le chronomètre et supprime l'affichage du temps de lecture
*/
function stopReadingTimer(element, text) {
if (readingTimers.has(element)) {
let { interval, counter } = readingTimers.get(element);
clearInterval(interval);
counter.remove();
readingTimers.delete(element);
}
}
/**
* Réinitialise tous les chronomètres lors du changement d'onglet
*/
function resetAllTimers() {
for (let [element, { interval, counter }] of readingTimers) {
clearInterval(interval);
counter.remove();
}
readingTimers.clear();
}
/**
* Supprime l'indicateur de lecture
*/
function removeReadingIndicator(element) {
if (readingTimers.has(element)) {
let { counter } = readingTimers.get(element);
counter.remove();
}
}
/**
* Fonction pour nettoyer le texte
*/
function cleanText(text) {
// Supprimer les puces et symboles inutiles
text = text.replace(/[\u2022\u00b7•·■◆▪▸▹▶►▻⇨]/g, " "); // Supprime puces et flèches
text = text.replace(/[\t\n\r]+/g, " "); // Supprime les sauts de ligne inutiles
text = text.replace(/\s{2,}/g, " "); // Remplace plusieurs espaces par un seul
// Essayer d'extraire le contenu utile des tableaux (ex: "1 | Chat | Animal" → "1 Chat Animal")
text = text.replace(/(\||\t)+/g, " "); // Remplace les séparateurs de tableau par un espace
text = text.replace(/(\s*-+\s*)+/g, " "); // Supprime les lignes de séparation des tableaux
// Supprimer les espaces en début et fin de texte
text = text.trim();
return text;
}
// STATISTIQUES ------------------------------------------------------------------------------------------
async function initializeTrackingState() {
const { isTrackingActive: storedState } = await browser.storage.local.get("isTrackingActive");
isTrackingActive = storedState ?? false;
log("[Stats] État initial récupéré :", isTrackingActive);
if (isTrackingActive) {
startTracking();
} else {
stopTracking();
}
}
// ─────────────────────────────────────────────────────────────────────────────
// Gestion de l'activation/désactivation des statistiques
// ─────────────────────────────────────────────────────────────────────────────
function startTracking() {
log("[Stats] Suivi des statistiques activé.");
addViewportBorder();
attachScrollListener();
}
console.log("[Stats] Suivi des statistiques activé.");
addViewportBorder();
attachScrollListener();
}
function stopTracking() {
log("[Stats] Suivi des statistiques désactivé.");
console.log("[Stats] Suivi des statistiques désactivé.");
removeViewportBorder();
detachScrollListener();
}
......@@ -99,7 +235,7 @@ log("Script stats.js chargé !");
if (!scrollListenerAttached) {
window.addEventListener("scroll", trackVisibleContent);
scrollListenerAttached = true;
log("[Stats] Écouteur de défilement attaché.");
console.log("[Stats] Écouteur de défilement attaché.");
}
}
......@@ -107,11 +243,11 @@ log("Script stats.js chargé !");
if (scrollListenerAttached) {
window.removeEventListener("scroll", trackVisibleContent);
scrollListenerAttached = false;
log("[Stats] Écouteur de défilement détaché.");
console.log("[Stats] Écouteur de défilement détaché.");
}
}
// BORDURE ------------------------------------------------------------------------------------------
// Indice visuel des statistiques actives
function injectBorder() {
const css = `
#border-svg {
......@@ -194,33 +330,4 @@ log("Script stats.js chargé !");
svg.remove();
}
}
// GESTION DES MESSAGES ------------------------------------------------------------------------------------------
browser.runtime.onMessage.addListener((message) => {
if (message.command === "pyodide-simplemma-ready") {
log("[Stats] Pyodide et Simplemma prêt, démarrage du scraping : ");
browser.storage.local.get(["extensionActive", "accessToken"].then(({ extensionActive, accessToken }) => {
if (extensionActive && accessToken) {
simplemmaReady = true;
initializeTrackingState();
} else {
log("[Stats] Extension non activée ou utilisateur déconnecté, désactivation du tracking.")
}
}))
}
if (message.command === "update-stats") {
if (message.isActive && simplemmaReady) {
startTracking();
} else {
stopTracking();
}
}
});
})();
importScripts('../utils/logger.js');
console.log("✅ pyodide_worker.js chargé avec succès !");
// URL de la version Pyodide la plus récente
const LATEST_BASE_URL = "https://cdn.jsdelivr.net/pyodide/v0.27.2/full/";
let pyodide = null;
let pyodideLoaded = false; // Variable indiquant si Pyodide est en mémoire
let simplemmaLoaded = false; // Variable indiquant si simplemma est déjà installé
let pyodideLoaded = false; // Indique si Pyodide est en mémoire
let simplemmaLoaded = false; // Indique si simplemma est déjà installé
let storedFrequencies = {}; // Stockage des fréquences accumulées
let autoAddEnabled = false; // Ajout automatique désactivé par défaut
let isAuthenticated = false; // Non connecté par défaut
let userThreshold = 10; // Seuil par défaut
let trackedLanguages = []; // Aucune langue suivie par défaut
let notifiedWords = {}; // Stockage des mots déjà signalés (pour éviter les doublons)
let stoplistFr = new Set(); // Stockage optimisé de la stoplist
// Écouteur des messages reçus du background script
self.onmessage = async (event) => {
const data = event.data;
log("[WebWorker] Message reçu du Background:", data);
// Initialisation unique de Pyodide puis de Simplemma
if (data.command === "pyodide-simplemma") {
if (pyodideLoaded && simplemmaLoaded) {
log("[Worker] Pyodide et Simplemma déjà chargés.");
self.postMessage({ type: "pyodide-simplemma", status: "already_loaded", message: "Pyodide et Simplemma déjà en mémoire" });
return;
}
const data = event.data;
console.log("[WebWorker] Message reçu du Background:", data);
if (data.command === "pyodide-simplemma") {
if (pyodideLoaded && simplemmaLoaded) {
console.log("[Worker] Pyodide et Simplemma déjà chargés.");
self.postMessage({ type: "pyodide-simplemma", status: "already_loaded", message: "Pyodide et Simplemma déjà en mémoire" });
return;
}
try {
if (!pyodideLoaded) {
console.log("[Worker] Chargement de Pyodide...");
try {
if (!pyodideLoaded) {
log("[Worker] Chargement de Pyodide...");
importScripts(`${LATEST_BASE_URL}pyodide.js`);
pyodide = await loadPyodide({ indexURL: LATEST_BASE_URL });
await pyodide.loadPackage("lzma");
await pyodide.loadPackage("micropip");
pyodideLoaded = true;
log("[Worker] Pyodide chargé avec succès !");
}
if (!simplemmaLoaded) {
log("[Worker] Installation de simplemma...");
await pyodide.runPythonAsync(`
import micropip
import re
try:
print("Installation de simplemma...")
await micropip.install("simplemma")
print("Installation réussie.")
import simplemma
print("simplemma importé avec succès.")
def tokenize(text):
return re.findall(r"\\b\\w+\\b", text.lower())
#Phrase de test pour vérifier l'installation de simplemma
print("Phrase de test:")
phrase = "Le chat mange la pomme"
tokens = tokenize(phrase)
print("Tokens extraits :", tokens)
lemmatized_tokens = [simplemma.lemmatize(token, lang="fr") for token in tokens]
print("Tokens lemmatisés :", lemmatized_tokens)
lemmatized_tokens
except Exception as e:
print("Erreur lors de l'installation ou de l'importation :", e)
e
`);
simplemmaLoaded = true;
log("[Worker] Simplemma installé avec succès !");
}
// Envoyer confirmation au background script
self.postMessage({ type: "pyodide-simplemma", status: "success", message: "Pyodide et Simplemma chargés" });
} catch (error) {
console.error("[Worker] Erreur lors du chargement de Pyodide ou Simplemma :", error);
self.postMessage({ type: "pyodide-simplemma", status: "error", message: error.toString() });
importScripts(`${LATEST_BASE_URL}pyodide.js`);
} catch (err) {
console.error("[Worker] Erreur lors de l'import de pyodide.js :", err);
self.postMessage({ type: "pyodide-simplemma", status: "error", message: err.toString() });
return;
}
pyodide = await loadPyodide({ indexURL: LATEST_BASE_URL });
await pyodide.loadPackage("lzma");
await pyodide.loadPackage("micropip");
pyodideLoaded = true;
console.log("[Worker] Pyodide chargé avec succès !");
}
if (!simplemmaLoaded) {
console.log("[Worker] Installation de simplemma...");
// On encapsule la logique dans une fonction asynchrone pour faciliter l'usage d'await
await pyodide.runPythonAsync(`
import micropip
import asyncio
async def main():
print("Installation de simplemma...")
await micropip.install("simplemma")
print("Installation réussie.")
import simplemma
print("simplemma importé avec succès.")
# Test simple : extraction de tokens et lemmatisation
import re
def tokenize(text):
return re.findall(r"\\b\\w+\\b", text.lower())
phrase = "Le chat mange la pomme"
tokens = tokenize(phrase)
print("Tokens extraits :", tokens)
lemmatized_tokens = [simplemma.lemmatize(token, lang="fr") for token in tokens]
print("Tokens lemmatisés :", lemmatized_tokens)
return lemmatized_tokens
await main()
`);
simplemmaLoaded = true;
console.log("[Worker] Simplemma installé avec succès !");
}
// Envoyer confirmation au background script
self.postMessage({ type: "pyodide-simplemma", status: "success", message: "Pyodide et Simplemma chargés" });
} catch (error) {
console.error("[Worker] Erreur lors du chargement de Pyodide ou Simplemma :", error);
self.postMessage({ type: "pyodide-simplemma", status: "error", message: error.toString() });
}
}
// --- Traitement du texte envoyé par stats.js ---
if (data.command === "process-text") {
if (!pyodideLoaded) {
console.log("[Worker] Pyodide non chargé.");
self.postMessage({ type: "process-text", status: "error", message: "Pyodide pas encore chargé" });
return;
}
console.log("[Worker] Texte reçu pour analyse :", data.text);
try {
const stoplistArray = Array.from(stoplistFr);
console.log("Stoplist utilisée :", stoplistArray);
const result = await pyodide.runPythonAsync(`
import json
import re
import simplemma
from simplemma import langdetect
// Activer/désactiver les statistiques (stats.js)
if (data.command === "toggle-stats") {
log(`[WebWorker] Statistiques ${data.isActive ? "activées" : "désactivées"}`);
return;
def detect_language(text):
lang_scores = simplemma.langdetect(text, lang=("fr", "en", "es", "de", "it", "pt"))
return lang_scores[0][0] if lang_scores else "unk"
def tokenize(text):
return re.findall(r"\\b[a-zA-ZÀ-ÿ'-]+\\b", text.lower())
# Chargement de la stoplist
stoplist = set(json.loads('${JSON.stringify(stoplistArray)}'))
text = """${data.text.replace(/\"/g, '\\"')}"""
detected_lang = detect_language(text)
if detected_lang == "unk":
detected_lang = "other"
tokens = tokenize(text)
if detected_lang == "fr":
tokens = [token.lower().strip() for token in tokens if token.lower().strip() not in stoplist]
lemmatized_tokens = [simplemma.lemmatize(token, lang=detected_lang) for token in tokens]
freq = {}
for token in lemmatized_tokens:
if token not in stoplist:
freq[token] = freq.get(token, 0) + 1
json.dumps({"lang": detected_lang, "frequencies": freq}, ensure_ascii=False)
`);
const parsedResult = JSON.parse(result);
const detectedLang = parsedResult.lang;
if (!storedFrequencies[detectedLang]) {
storedFrequencies[detectedLang] = {};
}
for (const [word, count] of Object.entries(parsedResult.frequencies)) {
storedFrequencies[detectedLang][word] = (storedFrequencies[detectedLang][word] || 0) + count;
}
self.postMessage({ type: "update-frequencies", frequencies: storedFrequencies });
if (autoAddEnabled) {
checkThreshold(detectedLang);
}
} catch (error) {
console.error("[Worker] Erreur dans l'analyse du texte :", error);
}
}
if (data.command === "update-preferences") {
userThreshold = data.threshold;
trackedLanguages = data.trackedLanguages;
autoAddEnabled = data.autoAdd;
isAuthenticated = data.isAuthenticated;
console.log("[Worker] Mise à jour des préférences :", { userThreshold, trackedLanguages, autoAddEnabled, isAuthenticated });
}
if (data.command === "update-stoplist") {
stoplistFr = new Set(data.stoplist.map(word => word.toLowerCase().trim()));
console.log("[Worker] Stoplist FR mise à jour :", stoplistFr);
}
};
// --- Vérification du seuil et notification ---
function checkThreshold(lang) {
if (!autoAddEnabled || !isAuthenticated) {
console.log("[Worker] Auto-Add désactivé ou utilisateur non connecté. Aucune vérification de seuil.");
return;
}
if (!trackedLanguages.includes(lang)) {
console.log(`[Worker] La langue ${lang} n'est pas suivie.`);
return;
}
console.log(`[Worker] Vérification des fréquences pour la langue ${lang}...`);
let wordsAboveThreshold = {};
if (storedFrequencies[lang]) {
const exceededWords = Object.entries(storedFrequencies[lang])
.filter(([word, count]) => count >= userThreshold && !(notifiedWords[lang] && notifiedWords[lang].includes(word)))
.map(([word]) => word);
if (exceededWords.length > 0) {
if (!notifiedWords[lang]) {
notifiedWords[lang] = [];
}
notifiedWords[lang].push(...exceededWords);
wordsAboveThreshold[lang] = exceededWords;
}
}
if (Object.keys(wordsAboveThreshold).length > 0) {
console.log("[Worker] Nouveaux mots dépassant le seuil :", wordsAboveThreshold);
self.postMessage({ type: "threshold-exceeded", wordsAboveThreshold: wordsAboveThreshold });
} else {
console.log("[Worker] Aucun nouveau mot n'a dépassé le seuil.");
}
}
alors
au
aucuns
aussi
autre
avant
avec
avoir
bon
car
ce
cela
ces
ceux
chaque
ci
comme
comment
dans
des
du
dedans
dehors
depuis
devrait
doit
donc
dos
début
elle
elles
en
encore
essai
est
et
eu
fait
faites
fois
font
hors
ici
il
ils
je
juste
la
le
les
leur
ma
maintenant
mais
mes
mien
moins
mon
mot
même
ni
nommés
notre
nous
ou
par
parce
pas
peut
peu
plupart
pour
pourquoi
quand
que
quel
quelle
quelles
quels
qui
sa
sans
ses
seulement
si
sien
son
sont
sous
soyez
sujet
sur
ta
tandis
tellement
tels
tes
ton
tous
tout
trop
très
tu
voient
vont
votre
vous
vu
ça
étaient
état
étions
été
être
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment