107 lines
3.8 KiB
JavaScript
107 lines
3.8 KiB
JavaScript
|
|
/* WEVAL Typographic Apostrophe Scanner v109 — 2026-04-18
|
|
Cosmetic client-side fix for French elisions in React SPA DOM.
|
|
Fixes both text nodes AND key HTML attributes (alt, title, aria-label, placeholder).
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
if (window !== window.top) return;
|
|
|
|
var TYPO = '\u2019';
|
|
var PATTERNS = [
|
|
[/\bl'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'l' + TYPO],
|
|
[/\bL'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'L' + TYPO],
|
|
[/\bd'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'd' + TYPO],
|
|
[/\bD'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'D' + TYPO],
|
|
[/\bn'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'n' + TYPO],
|
|
[/\bN'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'N' + TYPO],
|
|
[/\bs'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 's' + TYPO],
|
|
[/\bS'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'S' + TYPO],
|
|
[/\bc'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'c' + TYPO],
|
|
[/\bC'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'C' + TYPO],
|
|
[/\bm'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'm' + TYPO],
|
|
[/\bj'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'j' + TYPO],
|
|
[/\bt'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 't' + TYPO],
|
|
[/\bqu'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'qu' + TYPO],
|
|
[/\bQu'(?=[aeiouyhAEIOUYHéèêëîïôùûü])/g, 'Qu' + TYPO],
|
|
[/\bjusqu'(?=\w)/g, 'jusqu' + TYPO],
|
|
[/\bJusqu'(?=\w)/g, 'Jusqu' + TYPO],
|
|
[/\blorsqu'(?=\w)/g, 'lorsqu' + TYPO],
|
|
[/\bpuisqu'(?=\w)/g, 'puisqu' + TYPO],
|
|
[/\baujourd'(?=h)/g, 'aujourd' + TYPO],
|
|
[/\bquelqu'(?=[ue])/g, 'quelqu' + TYPO]
|
|
];
|
|
|
|
var SKIP_TAGS = { SCRIPT: 1, STYLE: 1, CODE: 1, PRE: 1, TEXTAREA: 1, INPUT: 1, NOSCRIPT: 1 };
|
|
var FIX_ATTRS = ['alt', 'title', 'aria-label', 'placeholder'];
|
|
|
|
function fix(text) {
|
|
if (!text || text.indexOf("'") === -1) return text;
|
|
var out = text;
|
|
for (var i = 0; i < PATTERNS.length; i++) {
|
|
out = out.replace(PATTERNS[i][0], PATTERNS[i][1]);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function fixTextNode(node) {
|
|
var v = node.nodeValue;
|
|
var fv = fix(v);
|
|
if (fv !== v) node.nodeValue = fv;
|
|
}
|
|
|
|
function fixAttrs(el) {
|
|
if (!el.getAttribute) return;
|
|
for (var i = 0; i < FIX_ATTRS.length; i++) {
|
|
var a = FIX_ATTRS[i];
|
|
var v = el.getAttribute(a);
|
|
if (v) {
|
|
var fv = fix(v);
|
|
if (fv !== v) el.setAttribute(a, fv);
|
|
}
|
|
}
|
|
}
|
|
|
|
function walk(root) {
|
|
if (!root || SKIP_TAGS[root.nodeName]) return;
|
|
// Text nodes
|
|
var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
|
acceptNode: function (n) {
|
|
return SKIP_TAGS[n.parentNode && n.parentNode.nodeName] ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
|
|
}
|
|
});
|
|
var n;
|
|
while ((n = walker.nextNode())) fixTextNode(n);
|
|
// Elements (for attributes)
|
|
var elWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
|
var el;
|
|
while ((el = elWalker.nextNode())) fixAttrs(el);
|
|
}
|
|
|
|
function run() {
|
|
try { walk(document.body); } catch (e) {
|
|
if (window.console) console.warn('[wv-apos] error', e);
|
|
}
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', function () { setTimeout(run, 400); });
|
|
} else {
|
|
setTimeout(run, 400);
|
|
}
|
|
|
|
var debounceTimer = null;
|
|
var observer = new MutationObserver(function () {
|
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
debounceTimer = setTimeout(run, 350);
|
|
});
|
|
|
|
if (document.body) {
|
|
observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: FIX_ATTRS });
|
|
} else {
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: FIX_ATTRS });
|
|
});
|
|
}
|
|
})();
|