MediaWiki:Common.js: Difference between revisions

Jump to navigation Jump to search
2,715 bytes removed ,  06:12, 17 December 2025
Undo revision 104883 by Gurwinder (talk)
No edit summary
(Undo revision 104883 by Gurwinder (talk))
Tag: Undo
 
(4 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 (window, document) {
(function () {
     'use strict';
     'use strict';


    // Small API object for testing/rerun
     function convertSingleQuotesText(text) {
    window.smartQuotes = window.smartQuotes || {};
 
    // Convert single quotes in a text string to smart quotes
     function convertSingleQuotes(text) {
         if (!text || text.indexOf("'") === -1) return text;
         if (!text || text.indexOf("'") === -1) return text;
        // Protect contractions/possessives like don't or John's
         text = text.replace(/([A-Za-z0-9])'([A-Za-z0-9])/g, "$1’$2"); // contractions/possessives
         text = text.replace(/([A-Za-z0-9])'([A-Za-z0-9])/g, "$1’$2");
         text = text.replace(/(^|[\s\(\[\{\<\u2014\u2013"“'«])'(?=\S)/g, "$1‘"); // opening quotes
        // Opening single quote when at start or after whitespace/open punctuation
         text = text.replace(/'/g, "’"); // remaining closing quotes
         text = text.replace(/(^|[\s\(\[\{\<\u2014\u2013"“'«])'(?=\S)/g, "$1‘");
        // Remaining single quotes are closing
         text = text.replace(/'/g, "’");
         return text;
         return text;
     }
     }


    // Walk text nodes under root and replace text content (skip code-like tags)
     function walkAndReplace(root) {
     function walkAndReplace(root) {
         if (!root) return;
         if (!root) return;
         try {
         try {
             var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
             var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
             var node;
             var n;
             var blacklist = new Set(["CODE", "PRE", "SCRIPT", "STYLE", "TEXTAREA", "NOSCRIPT", "MATH", "INPUT", "TEXTAREA"]);
             var blacklist = new Set(["CODE", "PRE", "SCRIPT", "STYLE", "TEXTAREA", "NOSCRIPT", "MATH", "INPUT"]);
             while ((node = walker.nextNode())) {
             while ((n = walker.nextNode())) {
                 var parent = node.parentNode;
                 var parent = n.parentNode;
                 if (!parent) continue;
                 if (!parent) continue;
                // Skip nodes with blacklisted ancestor
                 var anc = parent, skip = false;
                 var anc = parent, skip = false;
                 while (anc && anc.nodeType === 1) {
                 while (anc && anc.nodeType === 1) {
Line 817: Line 821:
                 }
                 }
                 if (skip) continue;
                 if (skip) continue;
                 var original = node.nodeValue;
                 var orig = n.nodeValue;
                 var replaced = convertSingleQuotes(original);
                 var rep = convertSingleQuotesText(orig);
                 if (replaced !== original) node.nodeValue = replaced;
                 if (rep !== orig) n.nodeValue = rep;
             }
             }
         } catch (e) {
         } catch (e) {
             // Defensive: log but don't throw
             console.error('smartQuotes error', e);
            if (window.console && window.console.error) {
                console.error('smartQuotes.walkAndReplace error:', e);
            }
         }
         }
     }
     }


    // Process main content area (parser output) then body fallback
     function applyOnce() {
     function processOnce() {
         var root = document.querySelector('.mw-parser-output') || document.body;
         var root = document.querySelector('.mw-parser-output') || document.body;
         walkAndReplace(root);
         walkAndReplace(root);
     }
     }


     // Observe for dynamic/AJAX content
     // run after window.load, also watch for AJAX content
     function initObserver() {
     window.addEventListener('load', function () {
         try {
         setTimeout(applyOnce, 150);
            var obs = new MutationObserver(function (mutations) {
        var obs = new MutationObserver(function (mutations) {
                mutations.forEach(function (m) {
            mutations.forEach(function (m) {
                    m.addedNodes && m.addedNodes.forEach(function (n) {
                m.addedNodes && m.addedNodes.forEach(function (n) {
                        if (n.nodeType === 1) walkAndReplace(n);
                    if (n.nodeType === 1) walkAndReplace(n);
                        else if (n.nodeType === 3 && n.parentNode) walkAndReplace(n.parentNode);
                    else if (n.nodeType === 3 && n.parentNode) walkAndReplace(n.parentNode);
                    });
                 });
                 });
             });
             });
            obs.observe(document.body, { childList: true, subtree: true });
        });
            window.smartQuotes._observer = obs;
        obs.observe(document.body, { childList: true, subtree: true });
        } catch (e) {
     }, false);
            console.error('smartQuotes.initObserver error:', e);
})();
        }
    }
 
    // Public API to run now (useful to trigger from console)
    window.smartQuotes.runNow = function () {
        processOnce();
        if (!window.smartQuotes._observer) initObserver();
        return true;
     };
 
    // Run after full window load — this avoids ResourceLoader bundle errors
    function runAfterLoad() {
        try {
            // Use requestIdleCallback if available for low priority, fallback to setTimeout
            var run = function () { window.smartQuotes.runNow(); };
            if ('requestIdleCallback' in window) {
                requestIdleCallback(run, { timeout: 2000 });
            } else {
                // slight delay to allow any late DOM insertions
                setTimeout(run, 350);
            }
            if (window.console) console.info('smartQuotes: scheduled run after load.');
        } catch (e) {
            console.error('smartQuotes.runAfterLoad error:', e);
        }
    }
 
    if (document.readyState === 'complete') {
        runAfterLoad();
    } else {
        // Attach to load to guarantee it runs after everything (including ResourceLoader errors)
        window.addEventListener('load', runAfterLoad, false);
        // Also run on DOMContentLoaded as a fallback
        document.addEventListener('DOMContentLoaded', function () {
            // still defer slightly so other inline scripts finish
            setTimeout(runAfterLoad, 150);
        }, false);
    }
 
    // Helpful console test: window.smartQuotes.test()
    window.smartQuotes.test = function (input) {
        input = input || " 'Hello' It's John's 'quote' ";
        var out = convertSingleQuotes(input);
        console.log('smartQuotes.test input:', input);
        console.log('smartQuotes.test output:', out);
        return out;
    };
 
})(window, document);

Navigation menu