ميدياويكي:Gadget-Numeral converter.js

من ويكيبيديا، الموسوعة الحرة

ملاحظة: بعد الحفظ، قد يلزمك إفراغ الكاش لرؤية التغييرات.

/**
 * Numeral converter
 * forked from [[mw:MediaWiki:Gadget-Numerakri.js]]
 * maintainer حبيشان
 */
mw.loader.using(['mediawiki.cookie', 'mediawiki.util', 'mediawiki.user']).then(function () {
    'use strict';
    const settingkey = 'gadget-Numeral_converter',
        skipclass = 'mwgadget-numconv-skip',
        msgs = {
            'option-1': {
                ar: 'تلقائي'
            },
            'option-2': {
                ar: '۱۲۳'
            },
            'option-3': {
                ar: '123'
            },
            'label-text': {
                ar: 'الأرقام: '
            },
            'label-tooltip': {
                ar: '١٢٣<->123'
            }
        },
        maps = {
            '2': ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩', '٪'],
            '3': ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '%']
        },
        rejectedTags = ['input', 'textarea', 'style', 'script', 'pre'];

    var currentType = '1',
        otherType,
        matchers = {},
        walker,
        styleTag;

    function isValidType(type) {
        return Number(type)>0 && Number(type)<4;
    }

    /** @return {Object.<RegExp>} */
    function getMatchers(targetType) {
        var rChars;
        if (!matchers[targetType]) {
            rChars = { 0: [], 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [], 10: [] };
            $.each(maps, function (type, map) {
                if (type !== targetType) {
                    for (var i = 0; i <= 10; i++) {
                        rChars[i].push(map[i]);
                    }
                }
            });
            rChars = $.map(rChars, function (chars) {
                return new RegExp('(' + $.map(chars, mw.util.escapeRegExp).join('|') + ')', 'g');
            });
            matchers[targetType] = rChars;
        }
        return matchers[targetType];
    }

    function msg(key) {
        return msgs[key][mw.config.get('wgUserLanguage')] || msgs[key].en;
    }

    /**
     * @param {HTMLElement|TextNode} node
     * @return {number} NodeFilter.FILTER_* constant
     */
    function filterNode(node) {
        if (node.nodeType === Node.TEXT_NODE) {
            return NodeFilter.FILTER_ACCEPT;
        }
        var n = node.nodeName && node.nodeName.toLowerCase();
        if ( rejectedTags.indexOf(n) > -1 ||
            // node.hasAttribute('contenteditable') ||
            $(node).hasClass(skipclass)
        ) {
            // Skip this element and skip its children
            return NodeFilter.FILTER_REJECT;
        }
        // Skip this element, but check its children
        return NodeFilter.FILTER_SKIP;
    }

    /**
     * @param {TextNode} node
     */
    function handleTextNode(node) {
        var original = node.nodeValue,
            changed = original,
            // matchers = getMatchers(currentType),
            i = 0;
        for (; i <= 10; i++) {
            changed = changed.replaceAll(maps[otherType][i], maps[currentType][i]);
        }
        if (original !== changed) {
            node.nodeValue = changed;
        }
    }

    // https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw-method-requestIdleCallback
    function idleWalker(deadline) {
        var el;
        if (!walker) {
            return;
        }
        while (deadline.timeRemaining() > 0) {
            el = walker.nextNode();
            if (!el) {
                // Reached the end
                walker = null;
                return;
            }
            handleTextNode(el);
        }

        // The user may interact with the page. We pause so the browser can process
        // interaction. The text handler will continue after that.
        if (walker) {
            mw.requestIdleCallback(idleWalker);
        }
    }

    /**
     * Save a browser cookie for 30 days, or remove it.
     * @param {string|null} value
     */
    function saveType(value) {
        mw.requestIdleCallback(function () {
            if (mw.user.isAnon()) {
                mw.cookie.set(settingkey, value, { expires: 30 * 86400 , path: '/' });
            } else {
                new mw.Api().saveOption(settingkey, value);
                mw.user.options.set(settingkey, value);
            }
            mw.storage.session.set(settingkey, value);
        });
    }

    /**
     * @return {string}
     */
    function getStoredType() {
        var value = mw.user.isAnon() ? mw.cookie.get(settingkey, '1') : mw.user.options.get(settingkey) || '1';
        var svalue = mw.storage.session.get(settingkey);
        
        if (svalue && svalue !== value) {
            value = svalue;
            saveType(value);
        } else if (value !== null && !isValidType(value)) {
            // Remove bad cookie
            saveType('1');
            value = '1';
        }

        return value || '1';
    }

    function startPageConversion() {
        if (styleTag) {
            // Undo style for a previously selected type
            $(styleTag).remove();
            styleTag = null;
        }

        if (currentType === '1') {
            // Don't change the page
            return;
        }

        otherType = (currentType == '3')? '2': '3';
        switch (currentType) {
            case '2':
                $('ol:lang(ar) li, ol.references, li.references').css('list-style-type', 'arabic-indic');
                break;
            case '3':
                $('ol:lang(ar) li, ol.references, li.references').css('list-style-type', 'decimal');
                break;
        }
        // If a walker is already active, replace it.
        // If no walker is active yet, start it.
        if (!walker) {
            mw.requestIdleCallback(idleWalker);
        }
        walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ALL, filterNode, false);
    }

    /** @return {HTMLElement} */
    function createSelector() {
        var $select = $('<select>').addClass(skipclass).append(
            $('<option>').val('1').text(msg('option-1'))
        );
        Object.keys(maps).forEach(function (type) {
            $select.append($('<option>').val(type).text(msg('option-' + type)));
        });
        $select.val(currentType);
        $select.on('change', function () {
            currentType = this.value;
            startPageConversion();
            saveType(currentType);
        });
        return $select[0];
    }

    function init() {
        var potlet;

        // Decide selected type
        currentType = getStoredType();
        startPageConversion();

        potlet = mw.util.addPortletLink(
            mw.user.isAnon() ? 'p-user-menu-anon-editor' : 'p-personal',
            '#',
            msg('label-text'),
            'pt-numconvert',
            msg('label-tooltip'),
            null,
            mw.user.isAnon() ?
                '#pt-createaccount' :
                mw.config.get('skin') === 'vector-2022' ?
                    '#pt-preferences' :
                    '#pt-userpage'
        );

        if (potlet) {
            $(potlet).html(msg('label-text'))
            potlet.appendChild(createSelector());
        }
    }

    $(function () {
        mw.requestIdleCallback(init);
    });
});