1(function ($) { 2 Drupal.behaviors.ssks_gtranslate_notranslate = { 3 attach: function (context, settings) { 4 excludeEmailLinksTranslation(); 5 protectExcludedWords(context, settings); 6 }, 7 }; 8 9 function excludeEmailLinksTranslation() { 10 // add class notranslate to all email 11 $(once("notranlate-class-mail", "a[href*=mailto]")).each(function () { 12 $(this).addClass("notranslate"); 13 }); 14 } 15 16 function protectExcludedWords(context, settings) { 17 // Get all excluded words, clean them (remove empty string, escape regex chars, order them) 18 let excluded_words = settings.ssks_gtranslate.excluded_words; 19 excluded_words = cleanWords(excluded_words); 20 if (!excluded_words.length) return; 21 22 // Get the target element from the context. 23 // Can be the document or a DOM element 24 // Typically to process ajax content, like the widget horaires 25 const $targetElement = (context === document) ? $("body") : $(context); 26 // Build the regex to match at least one word 27 const wordsRegex = new RegExp(`(?:^|[^a-zA-Z\\d'ââ²])(${excluded_words.join('|')})(?:[^a-zA-Z\\d]|$)`, 'i'); 28 $(once("notranlate-class-added", $targetElement)).each((k, el) => { 29 const queue = [el]; 30 let curr; 31 while ((curr = queue.pop())) { 32 // Utiliser TreeWalker pour ne traiter que les nÅuds de texte 33 const walker = document.createTreeWalker( 34 curr, 35 NodeFilter.SHOW_TEXT, 36 { 37 acceptNode: function (node) { 38 const parent = node.parentElement; 39 if (!parent) return NodeFilter.FILTER_ACCEPT; 40 41 // Skip si le parent est déjà notranslate 42 if (parent.classList.contains('notranslate')) { 43 return NodeFilter.FILTER_REJECT; 44 } 45 46 // Skip les contenus bruts non-DOM : noscript, script, style, template 47 // (leur "texte" peut contenir du HTML brut, ex: fallback d'images lazy-load) 48 const tag = parent.tagName; 49 if (tag === 'NOSCRIPT' || tag === 'SCRIPT' || tag === 'STYLE' || tag === 'TEMPLATE') { 50 return NodeFilter.FILTER_REJECT; 51 } 52 53 return NodeFilter.FILTER_ACCEPT; 54 } 55 }, 56 false 57 ); 58 59 // On collecte d'abord tous les nÅuds à traiter SANS muter le DOM : 60 // remplacer un nÅud en cours de traversée détache le nÅud courant du 61 // TreeWalker, et nextNode() s'arrête alors après le premier remplacement. 62 const nodesToProcess = []; 63 let textNode; 64 while ((textNode = walker.nextNode())) { 65 if (textNode.textContent.match(new RegExp(wordsRegex.source, "gi"))) { 66 nodesToProcess.push(textNode); 67 } 68 } 69 70 // La traversée terminée, on peut muter le DOM sans casser le walker. 71 nodesToProcess.forEach((node) => { 72 const textRegex = new RegExp(wordsRegex.source, "gi"); 73 // Créer un fragment de document pour le nouveau contenu 74 const fragment = document.createDocumentFragment(); 75 const tempDiv = document.createElement('div'); 76 tempDiv.innerHTML = node.textContent.replace( 77 textRegex, 78 (match) => `<span class="notranslate">${match}</span>` 79 ); 80 81 // Transférer les nÅuds du div temporaire vers le fragment 82 while (tempDiv.firstChild) { 83 fragment.appendChild(tempDiv.firstChild); 84 } 85 86 // Remplacer le nÅud de texte par le fragment 87 node.parentNode.replaceChild(fragment, node); 88 }); 89 } 90 }); 91 } 92 93 /** 94 * Cleans up and processes an array of words by removing empty strings, 95 * sorting them by length in descending order, and escaping special 96 * characters. 97 * 98 * @param {string[]} words - The array of words to be cleaned and processed. 99 * @return {string[]} The cleaned and processed array of words. 100 */ 101 function cleanWords(words) { 102 // Order words by length, bigger to smaller, to be sure to parse all words 103 // Exemple : parse "Le Rocher de Palmer" before "Le Rocher" 104 return words.filter((w) => w.trim().length) 105 .sort((a, b) => b.length - a.length) 106 .map((str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')); 107 } 108 109})(jQuery, Drupal, drupalSettings);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.