MediaWiki:Common.js: Difference between revisions

no edit summary
No edit summary
No edit summary
Line 783: Line 783:


( function () {
( function () {
     function convertSingleQuotes(text) {
     function smartSingleQuotes(text) {
         // Opening single quote -> ‘ when at start or after whitespace/opening punctuation and before letter/digit
         // Handle apostrophes in contractions or possessives first (like John's, it's)
         text = text.replace(/(^|[\s\(\[\{\"'«—])'(?=[A-Za-z0-9])/g, '$1‘');
         text = text.replace(/(\w)'(\w)/g, "$1’$2");
         // Closing single quote -> ’ when after letter/digit
 
         text = text.replace(/(?<=[A-Za-z0-9])'(?=$|[^\w\d])/g, '’');
         // Then handle opening quotes (at start or after space/open punctuation)
         // fallback
         text = text.replace(/(^|[\s\(\[\{\"‘“])/g, function(match) {
         text = text.replace(/'/g, '’');
            return match; // just return the same, will handle in next step
         return text;
        });
 
         // Now process alternation: alternate between opening ‘ and closing ’
         let result = "";
        let isOpen = true;
        for (let char of text) {
            if (char === "'") {
                result += isOpen ? "‘" : "";
                isOpen = !isOpen;
            } else {
                result += char;
            }
        }
         return result;
     }
     }


     function walkAndReplace(root) {
     function walkAndReplace(root) {
         var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
         const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
         var node;
         const blacklist = new Set(["CODE", "PRE", "SCRIPT", "STYLE", "TEXTAREA", "NOSCRIPT", "MATH"]);
        var blacklist = new Set(['CODE','PRE','SCRIPT','STYLE','TEXTAREA','NOSCRIPT','MATH','SPAN']); // adjust span if needed


         while (node = walker.nextNode()) {
        let node;
             var parent = node.parentNode;
         while ((node = walker.nextNode())) {
             if (!parent) continue;
             let parent = node.parentNode;
            if (blacklist.has(parent.nodeName)) continue;
             if (!parent || blacklist.has(parent.nodeName)) continue;
            // skip if inside a link? (optional)
            var ancestor = parent;
            var skip = false;
            while (ancestor && ancestor !== root) {
                if (blacklist.has(ancestor.nodeName)) { skip = true; break; }
                ancestor = ancestor.parentNode;
            }
            if (skip) continue;


             var newText = convertSingleQuotes(node.nodeValue);
             let newText = smartSingleQuotes(node.nodeValue);
             if (newText !== node.nodeValue) node.nodeValue = newText;
             if (newText !== node.nodeValue) node.nodeValue = newText;
         }
         }
     }
     }


    // Run after page load and on ajax content updates
     mw.hook("wikipage.content").add(function ($content) {
     mw.hook('wikipage.content').add(function($content) {
         walkAndReplace($content[0]);
         walkAndReplace($content[0]);
     });
     });
     document.addEventListener('DOMContentLoaded', function() {
 
     document.addEventListener("DOMContentLoaded", function () {
         walkAndReplace(document.body);
         walkAndReplace(document.body);
     });
     });
}() );
})();