User:Vanished user 1929210/js/followredirect.js

注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google ChromeFirefoxMicrosoft EdgeSafari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。
/*
 * 重新定向追蹤工具
 *
 * 使用方法:編輯原始碼->熒幕右上角的「其他」->追蹤重新定向
 *
 * 亦可使用原始碼2017,但是目前不支援視覺化編輯
 *
 * 僅需要在以下兩種情況中使用:
 * 1. 在導航模板中,如果連結是重新定向或與條目名稱簡繁體不同,那麼它在對應條目中並不會變成粗體文字,因此需要修一修。
 * 2. 如果頁面中有重新定向連結,而且重新定向的目標也是本頁,那麼應當移除連結。
 * 其他情況不應使用,即[[Wikipedia:沒壞就不要修]]。
 */

// <pre>

mw.loader.using('jquery.ui').then(function() {
    'use strict';

    var UI = (function () {
        switch (mw.config.get('wgUserLanguage')) {
            case 'zh':
            case 'zh-cn':
            case 'zh-hans':
            case 'zh-my':
            case 'zh-sg':
                return {
                    TITLE: '追踪重定向',
                    WELCOME: '本工具用于把重定向链接纠正到页面本身,建议仅在导航模板中使用。',
                    LOADING: '正在查询页面标题...',
                    LINK: '链接',
                    DEAL: '处理方式',
                    PASS: '不处理',
                    CHANGETO: '去除链接:',
                    OK: '确认',
                    SUMMARY: '将重定向链接修改到页面本身 (via [[User:逆襲的天邪鬼/js/followredirect.js|followredirect]])',
                };
            case 'zh-hk':
            case 'zh-mo':
            case 'zh-tw':
            case 'zh-hant':
                return {
                    TITLE: '追蹤重新定向',
                    WELCOME: '本工具用於把重新定向連結糾正到頁面本身,建議僅在導航模板中使用。',
                    LOADING: '正在查詢頁面標題...',
                    LINK: '連結',
                    DEAL: '處理方式',
                    PASS: '不處理',
                    CHANGETO: '去除連結:',
                    OK: '確認',
                    SUMMARY: '將重新定向連結修改到頁面本身 (via [[User:逆襲的天邪鬼/js/followredirect.js|followredirect]])',
                };
            default:
                return {
                    TITLE: 'Follow redirects',
                    WELCOME: 'This tool is used for following redirects and replace with pagenames themselves.',
                    LOADING: 'Querying...',
                    LINK: 'Links',
                    DEAL: 'Changes',
                    PASS: 'Pass',
                    CHANGETO: 'Unlink: ',
                    OK: 'OK',
                    SUMMARY: 'Followed redirects and replace with page names themselves (via [[User:逆襲的天邪鬼/js/followredirect.js|followredirect]])',
                };
        }
    })();


    // 檢查給定的標題,挑出屬於重新定向的標題。如果頁面存在,那麼給出它們的最終目標。
    var checkTitles = function (titles, callback) {
        var queue = [];
        var i;
        var n;

        n = parseInt((titles.length+50)/50);
        for (i=0; i<n; i++) {
            queue.push(titles.slice(50*i, 50*(i+1)).join('|'));
        }

        var map = {}, map2 = {};
        var targets = {};

        var combine = function (arr, target) {
            if (arr) {
                for (var i=0; i<arr.length; i++) {
                    target[arr[i].from] = arr[i].to;
                }
            }
        };

        var follow = function (title) {
            var r = map[title];
            while (map[r]) {
                r = map[r];
            }
            return r;
        };

        var done = function () {
            var result = {};
            for (var i=0; i<titles.length; i++) {
                var title = titles[i];
                var newtitle = follow(title);
                // 以下幾種情況不要處理:
                // 僅僅差一個「:」
                // 含有「#」,而且實質上是同一頁面(如果不是同一頁面則把#補回來)
                // 僅僅是大小寫不同和簡繁體不同
                if (targets[newtitle] && newtitle !== map2[title]) {
                    result[title] = newtitle;
                    if (title.indexOf('#') > -1) {
                        result[title] = result[title] + '#' + title.split('#')[1];
                    }
                }
            }
            callback(result);
        };

        var errors = function () {
            if (--n === 0) {
                done();
            }
        };

        var process = function (data) {
            if (data.query) {
                combine(data.query.normalized, map);
                combine(data.query.converted, map);
                combine(data.query.redirects, map);

                combine(data.query.normalized, map2);
                combine(data.query.converted, map2);

                var pages = data.query.pages;
                for (var pageid in pages) {
                    if (parseInt(pageid) > 0) {
                        var page = pages[pageid];
                        targets[page.title] = true;
                    }
                }
            }
            if (--n === 0) {
                done();
            }
        };

        for (i=0; i<queue.length; i++) {
            $.ajax({
                url: mw.util.wikiScript('api'),
                data: {
                    action: 'query',
                    format: 'json',
                    redirects: true,
                    converttitles: true,
                    titles: queue[i],
                },
                dataType: 'json',
                type: 'POST',
                success: process,
                error: errors,
            });
        }
    };


    // 由於事情不像fixlinkstyle那樣複雜,所以用不著建構WikiDOM樹
    var segments = [];
    var alllinks = [];
    var redirectlinks = [];
    var extractLinks = function (text) {
        var i;
        var result = [];
        segments = [];
        alllinks = [];

        var re = /\[\[([^\[\n]*?)\]\]/g;
        var match;
        var extract = /^(.*?):(.*)/;
        var extract2 = /^([Ff]ile|[Ii]mage|文件|檔案|[Cc]ategory|分类|分類)/;
        var lastPos = 0;
        while ((match = re.exec(text)) !== null) {
            // 前文
            segments.push(text.substring(lastPos, re.lastIndex-match[0].length));

            // 連結
            segments.push(match[0]);
            lastPos = re.lastIndex;

            // 不要處理檔案和分類
            // 系統會自動忽略跨語言連結(query.interwiki),所以不用特殊處理
            var match2 = extract.exec(match[1]);
            if (!(match2 && match2[1].match(extract2))) {
                var link = {
                    position: segments.length - 1,
                };

                var sepPos = match[1].indexOf('|');
                if (sepPos === -1) {
                    link.target = match[1];
                    link.text = '';
                } else {
                    link.target = match[1].substring(0, sepPos);
                    link.text = match[1].substring(sepPos+1);
                }

                alllinks.push(link);
            }
        }
        // 末尾文字
        segments.push(text.substring(lastPos));
    };

    var parseLinks = function (callback) {
        var titles = [];
        for (var i=0; i<alllinks.length; i++) {
            titles.push(alllinks[i].target);
        }

        redirectlinks = [];
        checkTitles(titles, function (map) {
            for (var i=0; i<titles.length; i++) {
                if (map[titles[i]]) {
                    alllinks[i].newtarget = map[titles[i]];

                    if (alllinks[i].target.indexOf(':') === 0 && map[titles[i]].indexOf(':') !== 0) {
                        alllinks[i].newtarget = ':' + alllinks[i].newtarget;
                    }

                    redirectlinks.push(alllinks[i]);
                }
            }
            callback();
        });
    };

    var linkToText = function (link) {
        if (link.target === '__UNLINK__') {
            return link.text;
        }
        if (link.text && link.text != link.target) {
            return '[[' + link.target + '|' + link.text + ']]';
        } else {
            return '[[' + link.target + ']]';
        }
    };

    var setLink = function (link) {
        var temp = link.target;
        if (link.newtarget) {
            link.target = link.newtarget;
        }
        segments[link.position] = linkToText(link);
        link.target = temp;
    };

    var makeArticle = function () {
        return segments.join('');
    };


    var htmlEscape = function (str) {
        var t = {
            '<': '&lt;',
            '>': '&gt;',
            '&': '&amp;',
        };
        return str.replace(/[<>&]/g, function (match) { return t[match]; });
    };

    var jobid = 0;
    var curjobid = 0;
    var dialog = function () {
        if (wikitextEditor.mode === 'visual') {
            alert(UI.NoVE || 'VE');
            return;
        }

        if (document.getElementById('followredirect_dialog') === null) {
            $('body').append('<div id="followredirect_dialog" style="display:none;" title="' + UI.TITLE + '"></div>');
        }

        $('#followredirect_dialog').html('<p id="fd_loading">' + UI.LOADING + '</p>');

        jobid++;
        curjobid = jobid;

        $('#followredirect_dialog').dialog({
            modal: false,
            draggable: true,
            close: function () {
                jobid++;
                redirectlinks = [];
            },
            width: 700,
            height: 500,
            buttons: [],
        });

        var defaultToPass = mw.config.get('wgNamespaceNumber') !== 10;
        var thisPageName = mw.config.get('wgPageName');

        var text = wikitextEditor.text;
        extractLinks(text);
        parseLinks(function () {
            if (curjobid !== jobid) {
                return;
            }

            var $ui = $('<p>').html(UI.WELCOME);
            var $table = $('<table class="wikitable" style="width:100%;">');
            var site = mw.config.get('wgScript');
            var i;

            $table.append('<tr><th>#</th><th>' + UI.LINK + '</th><th>' + UI.DEAL + '</th></tr>');

            for (i=0; i<redirectlinks.length; i++) {
                var link = redirectlinks[i];
                $table.append($('<tr>').append(
                    '<td>' + (i+1) + '</td>',
                    '<td><a href="' + site + '/' + encodeURI(link.target) + '" target="_blank">' + htmlEscape(linkToText(link)) + '</a></td>',
                    $('<td>').append('<input type="radio" id="fd_r' + i + '_1" name="fd_r' + i + '" value="1">',
                        '<label for="fd_r' + i + '_1">' + UI.PASS + '</label>&nbsp;&nbsp;&nbsp;',
                        '<input type="radio" id="fd_r' + i + '_2" name="fd_r' + i + '" value="2">',
                        '<label for="fd_r' + i + '_2">[[' + htmlEscape(link.newtarget) + ']]<br>',
                        '<input type="radio" id="fd_r' + i + '_3" name="fd_r' + i + '" value="3">',
                        $('<input type="text" size="20" id="fd_t' + i + '_a" name="fd_t' + i + '_a">').val(link.newtarget),
                        '|',
                        $('<input type="text" size="20" id="fd_t' + i + '_b" name="fd_t' + i + '_b">').val(link.text || link.target),
                        '<br>',
                        '<input type="radio" id="fd_r' + i + '_4" name="fd_r' + i + '" value="4">',
                        '<label for="fd_r' + i + '_4">' + UI.CHANGETO + '</label>',
                        $('<input type="text" size="20" id="fd_r' + i + '_c" name="fd_r' + i + '_c">').val(link.text || link.target)
                    )
                ));
            }

            $ui.append($('<form id="fd_frm">').append($table)); //, '<button id="fd_submit" class="mw-ui-button mw-ui-progressive">' + UI.OK + '</button>');
            $('#fd_loading').replaceWith($ui);

            $('#followredirect_dialog').dialog('option', 'buttons', [
                {
                    text: UI.OK,
                    click: function() {
                        for (var i=0; i<redirectlinks.length; i++) {
                            var frm = document.forms.fd_frm;
                            var link = redirectlinks[i];
                            switch (frm['fd_r' + i].value) {
                                case '2':
                                    link.text = '';
                                    break;
                                case '3':
                                    link.newtarget = '';
                                    link.target = frm['fd_t' + i + '_a'].value;
                                    link.text = frm['fd_t' + i + '_b'].value;
                                    break;
                                case '4':
                                    link.newtarget = '';
                                    link.target = '__UNLINK__';
                                    link.text = frm['fd_r' + i + '_c'].value;
                                    break;
                                default:
                                    link.newtarget = '';
                            }
                            setLink(link);
                        }

                        wikitextEditor.text = makeArticle();
                        wikitextEditor.summary = UI.SUMMARY;
                        $(this).dialog('close');
                    },
                },
            ]);

            for (i=0; i<redirectlinks.length; i++) {
                var frm = document.forms.fd_frm;
                frm['fd_r' + i].value = defaultToPass ? '1' : '3';
                // 如果目標是本頁,直接unlink
                if (redirectlinks[i].newtarget === thisPageName) {
                    frm['fd_r' + i].value = '4';
                }
            }

            $('input:text', '#followredirect_dialog').click(function (e) {
                var name = this.id;
                var i = name.lastIndexOf('_');
                var id = name.slice(4, i);
                if (name.indexOf('fd_r') === 0) {
                    document.forms.fd_frm['fd_r' + id].value = 4;
                } else {
                    document.forms.fd_frm['fd_r' + id].value = 3;
                }
            });
        });
    };

    mw.hook('editorapi.ready').add(function () {
        $('#p-followredirects').remove();
        $(mw.util.addPortletLink('p-cactions', '#', UI.TITLE, 'p-followredirects')).click(function (e) {
            e.preventDefault();
            dialog();
        });

        jobid++;
        try {
            $('#followredirect_dialog').dialog('close');
        } catch (ex) {

        }
    });
    mw.loader.load('https://zh.wikipedia.org/w/index.php?title=User:逆襲的天邪鬼/js/EditorAPIs.js&action=raw&ctype=text/javascript');
});

// </pre>
// [[Category:维基脚本]]