/** * 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; });