MediaWiki:Common.js: Difference between revisions

441 bytes added ,  06:12, 17 December 2025
Undo revision 104883 by Gurwinder (talk)
No edit summary
(Undo revision 104883 by Gurwinder (talk))
Tag: Undo
 
(8 intermediate revisions by the same user not shown)
Line 263: Line 263:




$( function(){
$(function () {
// toggled by toggle button. also determines which toggle button image to use
var useCustom = false;
var useCustom = false;
 
// toggleImgs[0] to switch to custom, toggleImgs[1] to revert to default
var zoomInSVG =
var toggleImgs = [
'<svg width="22" height="22" viewBox="0 0 24 24">' +
'//storage.googleapis.com/material-icons/external-assets/v4/icons/svg/ic_search_black_24px.svg',
'<path d="M15.5 14h-.8l-.3-.3a6.5 6.5 0 1 0-.7.7l.3.3v.8L20 21.5 21.5 20z ' +
'//storage.googleapis.com/material-icons/external-assets/v4/icons/svg/ic_youtube_searched_for_black_24px.svg'
'M10 14a4 4 0 1 1 0-8 4 4 0 0 1 0 8z ' +
];
'M11 7H9v2H7v2h2v2h2v-2h2V9h-2z"/>' +
var zoomInImg = '//storage.googleapis.com/material-icons/external-assets/v4/icons/svg/ic_zoom_in_black_24px.svg';
'</svg>';
var zoomOutImg = '//storage.googleapis.com/material-icons/external-assets/v4/icons/svg/ic_zoom_out_black_24px.svg';
 
var zoomOutSVG =
// create DOM elements
'<svg width="22" height="22" viewBox="0 0 24 24">' +
$('#content').prepend('<div id="zoomButtons" style="z-index: 9999;">\
'<path d="M15.5 14h-.8l-.3-.3a6.5 6.5 0 1 0-.7.7l.3.3v.8L20 21.5 21.5 20z ' +
<img id="zoomInIcon" src="' + zoomInImg + '" alt="zoom in"">\
'M7 9h6v2H7z"/>' +
<img id="zoomOutIcon" src="' + zoomOutImg + '" alt="zoom out"">\
'</svg>';
<img id="toggleButton" src="' + toggleImgs[+ useCustom] + '" alt="toggle zoom">&nbsp</div>');
 
$('#zoomButtons').css({'float': 'right'});
var toggleSVG =
'<svg width="22" height="22" viewBox="0 0 24 24">' +
// find DOM elements used later
'<path d="M5 4h14v2H5zm4 4h6l-2 12h-2z"/>' +
'</svg>';
 
$('#content').prepend(
'<div id="zoomButtons" style="z-index:9999; float:right; display:flex; gap:8px; cursor:pointer;">' +
'<span id="zoomInIcon">' + zoomInSVG + '</span>' +
'<span id="zoomOutIcon">' + zoomOutSVG + '</span>' +
'<span id="toggleButton">' + toggleSVG + '</span>' +
'</div>'
);
 
var $bodyContent = $('.mw-body-content');
var $bodyContent = $('.mw-body-content');
var $toggleButton = $('#toggleButton');
 
var sizes = [parseFloat($bodyContent.css('font-size'))];
// sizes[0] is default, sizes[1] is custom
var sizes = [parseFloat($('.mw-body-content').css('font-size'))];
// default custom zoom of 2
sizes[1] = sizes[0] + 2;
sizes[1] = sizes[0] + 2;
 
// the + converts bool to 0 or 1 to use as array index
function updateSize() {
function updateSize() {
$bodyContent.css({'font-size':(sizes[+ useCustom] + 'pt')});
$bodyContent.css('font-size', sizes[+useCustom] + 'pt');
}
}
function toggle() {
function toggle() {
useCustom = !useCustom;
useCustom = !useCustom;
$toggleButton.attr('src',toggleImgs[+ useCustom]);
updateSize();
updateSize();
}
}
function zoom(dif) {
function zoom(dif) {
sizes[1] += dif;
sizes[1] += dif;
Line 308: Line 314:
}
}
}
}
 
$( '#zoomInIcon' ).on( 'click', function(){
$('#zoomInIcon').on('click', function () {
console.log("Zoom +");
zoom(1);
zoom(1);
});
});
 
$( '#zoomOutIcon' ).on( 'click', function(){
$('#zoomOutIcon').on('click', function () {
console.log("Zoom -");
zoom(-1);
zoom(-1);
});
});
 
$( '#toggleButton' ).on( 'click', toggle );
$('#toggleButton').on('click', toggle);
});
});




Line 781: Line 786:
});
});


// ==UserScript==
// @name        MediaWiki Smart Single Quotes
// @namespace    http://your.local/
// @version      1.0
// @description  Convert straight single quotes to typographic single quotes on MediaWiki pages (visual only).
// @match        https://wiki.ekatrafoundation.org/*
// @grant        none
// ==/UserScript==


( function () {
(function () {
     function smartSingleQuotes(text) {
     'use strict';
        // Handle apostrophes in contractions or possessives first (like John's, it's)
        text = text.replace(/(\w)'(\w)/g, "$1’$2");


         // Then handle opening quotes (at start or after space/open punctuation)
    function convertSingleQuotesText(text) {
         text = text.replace(/(^|[\s\(\[\{\"‘“])/g, function(match) {
         if (!text || text.indexOf("'") === -1) return text;
            return match; // just return the same, will handle in next step
        text = text.replace(/([A-Za-z0-9])'([A-Za-z0-9])/g, "$1’$2"); // contractions/possessives
        });
         text = text.replace(/(^|[\s\(\[\{\<\u2014\u2013"“'«])'(?=\S)/g, "$1‘"); // opening quotes
        text = text.replace(/'/g, "’"); // remaining closing quotes
        return text;
    }


         // Now process alternation: alternate between opening ‘ and closing ’
    function walkAndReplace(root) {
         let result = "";
         if (!root) return;
        let isOpen = true;
         try {
        for (let char of text) {
            var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
            if (char === "'") {
            var n;
                 result += isOpen ? "‘" : "’";
            var blacklist = new Set(["CODE", "PRE", "SCRIPT", "STYLE", "TEXTAREA", "NOSCRIPT", "MATH", "INPUT"]);
                 isOpen = !isOpen;
            while ((n = walker.nextNode())) {
            } else {
                var parent = n.parentNode;
                 result += char;
                if (!parent) continue;
                var anc = parent, skip = false;
                while (anc && anc.nodeType === 1) {
                    if (blacklist.has(anc.nodeName)) { skip = true; break; }
                    anc = anc.parentNode;
                }
                if (skip) continue;
                 var orig = n.nodeValue;
                 var rep = convertSingleQuotesText(orig);
                 if (rep !== orig) n.nodeValue = rep;
             }
             }
        } catch (e) {
            console.error('smartQuotes error', e);
         }
         }
        return result;
     }
     }


     function walkAndReplace(root) {
     function applyOnce() {
         const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
         var root = document.querySelector('.mw-parser-output') || document.body;
        const blacklist = new Set(["CODE", "PRE", "SCRIPT", "STYLE", "TEXTAREA", "NOSCRIPT", "MATH"]);
        walkAndReplace(root);
 
        let node;
        while ((node = walker.nextNode())) {
            let parent = node.parentNode;
            if (!parent || blacklist.has(parent.nodeName)) continue;
 
            let newText = smartSingleQuotes(node.nodeValue);
            if (newText !== node.nodeValue) node.nodeValue = newText;
        }
     }
     }


     mw.hook("wikipage.content").add(function ($content) {
     // run after window.load, also watch for AJAX content
         walkAndReplace($content[0]);
    window.addEventListener('load', function () {
    });
         setTimeout(applyOnce, 150);
 
        var obs = new MutationObserver(function (mutations) {
    document.addEventListener("DOMContentLoaded", function () {
            mutations.forEach(function (m) {
         walkAndReplace(document.body);
                m.addedNodes && m.addedNodes.forEach(function (n) {
     });
                    if (n.nodeType === 1) walkAndReplace(n);
                    else if (n.nodeType === 3 && n.parentNode) walkAndReplace(n.parentNode);
                });
            });
         });
        obs.observe(document.body, { childList: true, subtree: true });
     }, false);
})();
})();