跳转到内容

User:Former User aDB0haVymg/Gadgets/mwEmojiUI.js

维基百科,自由的百科全书

注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google ChromeFirefoxMicrosoft EdgeSafari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。

/**
 * 
 * MWEmojiUI.js
 * 
 * ----
 * 
 * Add tasteful emoji icons to MediaWiki's bland interface links
 * 
 * ----
 * 
 * @version 8
 * @author  User:Classy_Melissa at zh.100ke.info
 * @license CC-0 (Public Domain)
 * 
 */
/* globals mw */

// wrap in meta function to prevent scope leak
"use strict";
function mwEmojiUIContainerFunction () {

    // "global" config values
    const MWEMOJIUI_NUM_PADDING_SPACES = 1; // how many spaces to add after the emoji
    const MWEMOJIUI_MAX_CONSOLE_LOGS = 5; // maximum number of element-not-found msgs handed to the console

    // "global" static variables
    var numPrintedConsoleLog = 0;
    
    /**
     * Syntax of the "emoji map" object
     * [
     *  0:  target:     a CSS selector to a particular element, 
     *                  which will have its innerText amended with the emoji
     *  1:  string:     the string to prepend to the link text (usually an emoji)
     * ]
     */

    // all emoji maps. An array of "emoji match" objects
    const emojiMaps = [

        // sidebar
        ["#n-Main-Page a", "🏡"],
        ["#n-Recent-changes a", "🆕"],

        // navigation bar ("ca")
        ["#ca-view a", "📄"],
        ["#ca-talk a", "💬"],
        ["#ca-ve-edit a", "📝"],
        ["#ca-viewsource a", "👁‍🗨"],
        ["#ca-edit a", "👩‍💻"],
        ["#ca-history a", "🗄️"],
        ["#ca-delete a", "🗑️"],
        ["#ca-move a", "🔀"],
        ["#ca-protect a", "🔐"],
        ["#ca-unprotect a", "🔓"],

        // toolbar ("pt")
        ["#pt-mytalk a", "💬"],
        ["#pt-preferences a", "⚙️"],
        ["#pt-watchlist a", "👁️"],
        ["#pt-betafeatures a", "💡"],
        ["#pt-mycontris a", "💖"],
        ["#pt-logout a", "❌"],

        // category
        ["#mw-normal-catlinks", "📚"],

        // tools ("t")
        ["#t-whatlinkshere", "🕵️‍♀️"],
        ["#t-recentchangeslinked", "🆕"],
        ["#t-upload", "📤"],
        ["#t-specialpages", "⚙️"],
        ["#t-print", "🖨️"],
        ["#t-permalink", "⚓"],
        ["#t-info", "🔖"],
        ["#t-cite", "📎"],
        ["#t-contributions", "💖"],
        ["#t-log", "🧾"],
        ["#t-blockip", "🛑"],
        ["#t-userrights", "✅"],

        // zhwp specific styles
        // navigation portals ("n")
        ["#n-mainpage-description", "🏡"],
        ["#n-indexpage", "🗃"],
        ["#n-Featured_content", "⭐"],
        ["#n-currentevents", "📰"],
        ["#n-recentchanges", "🗓️"],
        ["#n-randompage", "🔀"]


    ];

    /**
     * Generates a <span> tag to contain the new emoji and padding whitespaces
     * @returns {HTMLElement}   an empty span
     */
    function makeContainerElement() {

        const newSpan = document.createElement("span");

        newSpan.style.display = "inline";
        newSpan.style.backgroundImage = "none";

        return newSpan;

    }

    /**
     * Logs to console that some selector is not found.
     * @param {String} targetSelector the selector that isn't found
     */
    function complainSelectorNotFound(targetSelector) {

        if (numPrintedConsoleLog <= MWEMOJIUI_MAX_CONSOLE_LOGS) {

            ++numPrintedConsoleLog;

            console.log(
                "MWEmojiUI.js Info: Element \"" + targetSelector + "\" doesn't seem to be present."
            );

        }

    }

    /**
     * Appends an emoji to an element specified by the map. 
     * Raises an exception if something goes wrong
     * @param {String[]} map 
     */
    function appendEmojiByMap(map) {

        // extract variables from the map array
        const targetSelector = map[0];
        const emojiToInsert = map[1];

        // try to find the element
        const targetElement = document.querySelector(targetSelector);

        // if the element is not found, 
        // then log a message in the console & quit
        if (!targetElement) {
            complainSelectorNotFound(targetSelector);
            return;
        }

        //-- the element is found --

        // calculate the new innerText string
        var newInnerTextString = "";
        newInnerTextString += emojiToInsert; // deep copy as a preventative measure

        // append padding spaces
        for (var i = 0; i < MWEMOJIUI_NUM_PADDING_SPACES; ++i) {
            newInnerTextString += ' ';
        }
        
        // create a new container element, and apply the new innerText string
        const newContainer = makeContainerElement();
        newContainer.innerText = newInnerTextString;

        // apply the change
        targetElement.insertAdjacentElement("afterbegin", newContainer);

        // done

    }

    /**
     * Returns a promise to process one item of the emojiMaps
     * @param {String[]} map    the map to process
     * @returns {Promise}       promise to process the map
     */
    function promiseToAppendEmojiByMap(map) {
        
        return new Promise(
            
            // it'll simply call processMap function. Reject iff there's a exception.
            // rejection should go all the way up to the original caller of ignite()
            function(resolve, reject) {

                try {

                    appendEmojiByMap(map);
                    resolve();

                } catch (exception) {

                    reject(exception);

                }

            }

        );

    }

    function ignite(key) {

        // promises to process each emojiMap
        var mapProcessPromises = [];

        // start the processes
        emojiMaps.forEach( function(thisMap) {
            mapProcessPromises.push( promiseToAppendEmojiByMap(thisMap) );
        } );

        // simply have whoever that called ignition handle the promises
        return Promise.all(mapProcessPromises);

    }

    // kickstart the operation
    ignite()/* Promise<> */.catch(

        // if one fails, notify the user
        function(rejectReason) {
            mw.notify("MWEmojiUI.js Error: " + rejectReason.toString());
        }

    );

}

mwEmojiUIContainerFunction();