- ✨ Ajout de la fonctionnalité de correction via le menu contextuel. - 🗑️ Suppression de la fonctionnalité de correction en temps réel. - ➕ Ajout du script de fond (background.js) pour gérer le menu contextuel. - 📝 Mise à jour du manifest.json (permissions, service_worker, description). - 🐛 Correction de la logique de remplacement de texte dans content.js pour une meilleure compatibilité. - 🏗️ Mise à jour du Dockerfile pour construire l'extension Chrome dans un répertoire (non zippé). - 🛠️ Mise à jour du script build.sh pour gérer le nouveau format de sortie et nettoyer le répertoire. - 🌐 Traduction du manifest.json en français. - 📄 Ajout d'un README.md complet avec les instructions d'installation. - 🐛 Ajout de logs pour faciliter le débogage.
122 lines
6.8 KiB
JavaScript
122 lines
6.8 KiB
JavaScript
/**
|
|
* This content script handles the conversion of selected text to use the inclusive middle dot.
|
|
* It receives messages from the background script to perform the conversion.
|
|
*/
|
|
|
|
/**
|
|
* Converts text to use the inclusive middle dot (·).
|
|
* This function contains the original conversion logic.
|
|
* @param {string} texte The input text.
|
|
* @returns {string} The converted text.
|
|
*/
|
|
function convertirPointMedian(texte) {
|
|
let currentText = texte;
|
|
const sep = /[.\-]/; // Recognizes '.' or '-' as separators
|
|
|
|
// Rules with 3 components (e.g., "mot.e.s")
|
|
const regles3 = [
|
|
{ find: new RegExp(`(\\w+)${sep.source}(\\w+[es]*)${sep.source}([es]+)`, 'g'), replace: '$1·$2·$3' },
|
|
{ find: new RegExp(`([td]eur)${sep.source}([td]rice)${sep.source}([s]+)`, 'g'), replace: '$1·$2·$3' },
|
|
{ find: new RegExp(`(eur)${sep.source}(ice|euse)${sep.source}([s]+)`, 'g'), replace: '$1·$2·$3' },
|
|
{ find: new RegExp(`(er)${sep.source}(ère)${sep.source}([s]+)`, 'g'), replace: '$1·$2·$3' },
|
|
{ find: new RegExp(`([fs])${sep.source}([fs]{2}e)${sep.source}([s]+)`, 'g'), replace: '$1·$2·$3' },
|
|
{ find: new RegExp(`([eo][lnt])${sep.source}([eo]?[lnt]+e)${sep.source}([s]+)`, 'g'), replace: '$1·$2·$3' }
|
|
];
|
|
regles3.forEach(rule => { currentText = currentText.replace(rule.find, rule.replace); });
|
|
|
|
// Rules with 2 components (e.g., "mot.s")
|
|
const regles2 = [
|
|
{ find: new RegExp(`(\\w+)${sep.source}([es]+)`, 'g'), replace: '$1·$2' },
|
|
{ find: new RegExp(`([td]eur[s]?)${sep.source}([td]?rice[s]?)`, 'g'), replace: '$1·$2' },
|
|
{ find: new RegExp(`(eur[s]?)${sep.source}(euse[s]?|ice[s]?)`, 'g'), replace: '$1·$2' },
|
|
{ find: new RegExp(`(eux|el)${sep.source}(elle[s]?|euse[s]?|le[s]?)`, 'g'), replace: '$1·$2' },
|
|
{ find: new RegExp(`(aux|al)${sep.source}(ale[s]?)`, 'g'), replace: '$1·$2' },
|
|
{ find: new RegExp(`(er[s]?)${sep.source}(ère[s]?)`, 'g'), replace: '$1·$2' },
|
|
{ find: new RegExp(`([s])${sep.source}([s]{2}e[s]?)`, 'g'), replace: '$1·$2' },
|
|
{ find: new RegExp(`([eo][flnt][s]?)${sep.source}([eo]?[flnt]+e[s]?)`, 'g'), replace: '$1·$2' },
|
|
{ find: new RegExp(`(\\w+e)${sep.source}(\\w+a)`, 'g'), replace: '$1·$2' },
|
|
{ find: new RegExp(`(\\w+t)${sep.source}(te)`, 'g'), replace: '$1·$2' }
|
|
];
|
|
regles2.forEach(rule => { currentText = currentText.replace(rule.find, rule.replace); });
|
|
|
|
// Rules using parentheses (e.g., "mot(s)")
|
|
const reglesp = [
|
|
{ find: /(\w+)[\(]([es]+)[\)]/g, replace: '$1·$2' },
|
|
{ find: /([td]eur[s]?)[\(]([td]?rice[s]?)[\)]/g, replace: '$1·$2' },
|
|
{ find: /(eur[s]?)[\(](euse[s]?|ice[s]?)[\)]/g, replace: '$1·$2' },
|
|
{ find: /(eux|el)[\(](elle[s]?|euse[s]?|le[s]?)[\)]/g, replace: '$1·$2' },
|
|
{ find: /(aux|al)[\(](ale[s]?)[\)]/g, replace: '$1·$2' },
|
|
{ find: /(er[s]?)[\(](ère[s]?)[\)]/g, replace: '$1·$2' },
|
|
{ find: /([s])[\(]([s]{2}e[s]?)[\)]/g, replace: '$1·$2' },
|
|
{ find: /([eo][flnt][s]?)[\(]([eo]?[flnt]+e[s]?)[\)]/g, replace: '$1·$2' },
|
|
{ find: /(\w+e)[\(](\w+a)[\)]/g, replace: '$1·$2' },
|
|
{ find: /(\w+t)[\(](te)[\)]/g, replace: '$1·$2' }
|
|
];
|
|
reglesp.forEach(rule => { currentText = currentText.replace(rule.find, rule.replace); });
|
|
|
|
return currentText;
|
|
}
|
|
|
|
// Listen for messages from the background script.
|
|
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
console.log("Content script: Message received.", request);
|
|
if (request.action === "convertSelectedText" && request.text) {
|
|
console.log(`Content script: Converting text: "${request.text}"`);
|
|
const convertedText = convertirPointMedian(request.text);
|
|
console.log(`Content script: Converted text: "${convertedText}"`);
|
|
|
|
// Try to use document.execCommand for better compatibility with contenteditable elements
|
|
// and to maintain undo/redo history.
|
|
if (document.execCommand('insertText', false, convertedText)) {
|
|
console.log("Content script: Text replaced using execCommand.");
|
|
// If execCommand was successful, dispatch an input event to notify frameworks.
|
|
const activeElement = document.activeElement;
|
|
if (activeElement) {
|
|
activeElement.dispatchEvent(new Event('input', { bubbles: true }));
|
|
console.log("Content script: Dispatched input event.");
|
|
}
|
|
sendResponse({ success: true, newText: convertedText });
|
|
} else {
|
|
// Fallback for elements where execCommand might not work or for direct text selections.
|
|
console.log("Content script: execCommand failed, falling back to window.getSelection.");
|
|
const selection = window.getSelection();
|
|
if (selection.rangeCount > 0) {
|
|
const range = selection.getRangeAt(0);
|
|
range.deleteContents(); // Remove the selected text
|
|
range.insertNode(document.createTextNode(convertedText)); // Insert the new text
|
|
console.log("Content script: Text replaced using window.getSelection.");
|
|
|
|
// Manually dispatch an input event if the target is an editable element
|
|
// and execCommand didn't handle it.
|
|
const activeElement = document.activeElement;
|
|
if (activeElement && (activeElement.isContentEditable ||
|
|
activeElement.tagName.toLowerCase() === 'textarea' ||
|
|
(activeElement.tagName.toLowerCase() === 'input' && /text|search|email|url|tel/.test(activeElement.type)))) {
|
|
activeElement.dispatchEvent(new Event('input', { bubbles: true }));
|
|
console.log("Content script: Dispatched input event (fallback).");
|
|
}
|
|
sendResponse({ success: true, newText: convertedText });
|
|
} else {
|
|
console.log("Content script: No selection found for fallback replacement.");
|
|
sendResponse({ success: false, error: "No selection found." });
|
|
}
|
|
}
|
|
|
|
// After replacement, clear the selection to prevent accidental re-insertion
|
|
// or issues with the "buffer" as described by the user.
|
|
const selection = window.getSelection();
|
|
if (selection) {
|
|
selection.empty(); // For Chrome/Safari
|
|
if (selection.removeAllRanges) { // For Firefox
|
|
selection.removeAllRanges();
|
|
}
|
|
console.log("Content script: Selection cleared.");
|
|
}
|
|
|
|
} else {
|
|
console.log("Content script: Message not for text conversion or missing text.", request);
|
|
sendResponse({ success: false, error: "Invalid request." });
|
|
}
|
|
// Return true to indicate that sendResponse will be called asynchronously.
|
|
return true;
|
|
});
|