User:胡葡萄/PageCollapser.js

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

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

/**
 * 頁面摺疊器 v0.2 by User:逆襲的天邪鬼
 *
 * 自己拿着開心去吧。記住不要用IE678那種老掉牙的瀏覽器。
 *
 * 功能:
 * 1. 將長長的討論頁或條目等頁面摺疊起來。
 * 2. 將「互煮」的討論審查掉。(可能會造成不良影響,慎用)
 *
 * 待添加功能:
 * 1. 使用交互式頁面設定「黑名單」,並通過LocalStorage存起來
 * 2. 不要摺疊封禁申訴、頁面底部導航等模板
 * 3. 不摺疊已存檔或已結束的話題等(因為已經被摺疊了)
 * 4. 針對DYK、VIP等特殊頁面進行特殊處理(例如給出處理結果等)
 *
 * 已知bug:
 * 1. DYK頁面無法摺疊(因為DYK頁面結構特殊,而且該頁面不能直接摺疊否則會Orz)
 * 2. 被框住的內容無法摺疊,例如[[User talk:和平奮鬥救地球]](問題出在第244行$content.children().each……)
 */

$(function() {
    'use strict';

    /**
     * 說明:由於沒設計交互式對話框,需要按照下面的內容進行設置。
     *
     * 設置時,可以在自己的common.js頁面中加入:
     * window.talkCollapser = {
     *     collapse: true,
     *     blacktitle: [
     *         'A',
     *         '.*B'
     *     ],
     *     ...
     * }
     *
     * 可摺疊標題的樣式由 .collapsable 類定義。自己去CSS頁設置就行。
     * 該程式支持標題「黑名單」功能,上榜的討論將會被直接隱藏。但程式不支持簡繁轉換,並且因為是RegExp的test,所以表達式不要寫得太簡單以免誤傷。
     */
    var defaultConfig = {
        collapse: false,
        arrow: {                // 展開/摺疊箭頭
            color: '#777',
            open:  '▾[已摺疊]',
            close: '▴'
        },
        duration: 'fast',       // 動畫效果持續時間
        hide: {
            article: true,     // 是否在普通條目中和論述類頁面中摺疊
            talk: true,         // 是否在討論頁面(包括討論頁、互助客棧等)中摺疊
            vote: true,         // 是否在投票頁(包括DYK、存廢討論等)中摺疊
            request: true,      // 是否在請求頁(包括VIP、RFCU等)中摺疊
            user: false         // 是否在User命名空間中摺疊
        },
        noh3: true,             // 鑑於有人會在討論頁中開小標題,該選項可以將其從目錄中移除。僅用於討論頁。
        censor: {               // 對黑名單的審查
            showTag: true,      // 為true時標出「已隱藏」,為false時直接隱藏,不留痕跡
            tagTitle: '[該討論已被隱藏]',
            tagDesc: '該討論已被隱藏,原標題為:$1',
        },
        blacktitle: [           // 黑名單
        ]
    };

    var userconfig = window.talkCollapser;
    if (typeof userconfig !== 'object') {
        userconfig = {};
    }

    var config = $.extend(true, defaultConfig, userconfig);

    var visible = {};

    var isNoToc = (document.getElementById('toc') === null);
    var titleLevel = 'h2';
    var hideH3 = config.noh3;
    var needCollapse = false;
    config.noh3 = false;
    var projects = {
        talk: {
            '互助客栈': 'h2',
            '知识问答': 'h2'
        },
        vote: {
            '新条目推荐/候选': 'h3',       // TODO: 單獨處理
            '優良條目評選': 'h3',
            '特色条目评选': 'h3',
            '同行评审': 'h3',
            '頁面存廢討論/記錄': 'h2',
            '頁面存廢討論/疑似侵權': 'h3',
            '檔案存廢討論': 'h2'
        },
        request: {
            '合并请求': 'h3',
            '请求保护页面': 'h3',
            '当前的破坏': 'h3',
            '用戶查核請求': 'h3'
        }
    };

    switch (mw.config.get('wgCanonicalNamespace')) {
        case 'Talk':
        case 'User_talk':
        case 'File_talk':
        case 'MediaWiki_talk':
        case 'Template_talk':
        case 'Help_talk':
        case 'Category_talk':
        case 'Portal_talk':
        case 'Draft_talk':
        case 'Module_talk':
        case 'Project_talk':
            needCollapse = config.hide.talk;
            config.noh3 = hideH3;
            break;
        case '':
        case 'Draft':
        case 'Help':
            needCollapse = config.hide.article;
            break;
        case 'User':
            needCollapse = (!isNoToc) && config.hide.user;
            break;
        case 'Project':
            var title = mw.config.get('wgTitle');
            var t;
            needCollapse = !isNoToc;
            for (t in projects.talk) {
                if (title.startsWith(t)) {
                    needCollapse = config.hide.talk;
                    titleLevel = projects.talk[t];
                    config.noh3 = hideH3;
                    break;
                }
            }
            for (t in projects.vote) {
                if (title.startsWith(t)) {
                    needCollapse = config.hide.vote;
                    titleLevel = projects.vote[t];
                    break;
                }
            }
            for (t in projects.request) {
                if (title.startsWith(t)) {
                    needCollapse = config.hide.request;
                    titleLevel = projects.request[t];
                    break;
                }
            }
            break;
        default:
            needCollapse = false;
            break;
    }

    if (needCollapse && mw.config.get('wgAction') === 'view') {
        // 折叠讨论串

        var $content = $('#mw-content-text');

        var firstTopic = true;
        var $lastTitle = null, $discuss = null;

        // 找到第一个h2
        var childrenCount = $content.children().length;

        // 處理黑名單
        var blacklist = [];
        for (var i=0; i<config.blacktitle.length; i++) {
            blacklist.push(new RegExp(config.blacktitle[i]));
        }

        // 摺疊或移除討論
        var makeBox = function () {
            if ($lastTitle === null) {
                return;
            }

            var titleId = $lastTitle.attr('data-id');
            var titleName = $lastTitle.children().first().text();
            var badTitle = false;

            // 判斷是否為黑名單標題
            for (var i=0; i<blacklist.length; i++) {
                if (blacklist[i].test(titleName)) {
                    badTitle = true;
                    break;
                }
            }

            var $tocTitle = $("a[href='#" + titleId + "']", '#toc');
            if (config.noh3) {
                $('ul', $tocTitle.parent()).remove();
            }

            // 處理符合黑名單的討論
            if (badTitle) {
                if (config.censor.showTag) {
                    var $censor = $('<table class="plainlinks ombox ombox-notice" style=""><tbody><tr><td class="mbox-image"><a href="/wiki/File:Information_icon4.svg" class="image"><img alt="Information icon4.svg" src="//upload.wikimedia.org/wikipedia/commons/thumb/1/1d/Information_icon4.svg/40px-Information_icon4.svg.png" width="40" height="40" srcset="//upload.wikimedia.org/wikipedia/commons/thumb/1/1d/Information_icon4.svg/60px-Information_icon4.svg.png 1.5x, //upload.wikimedia.org/wikipedia/commons/thumb/1/1d/Information_icon4.svg/80px-Information_icon4.svg.png 2x" data-file-width="620" data-file-height="620"></a></td><td class="mbox-text" style="">{{{text}}}</td></tr></tbody></table>');
                    $('.mbox-text', $censor).text(config.censor.tagDesc.replace('$1', titleName));
                    $discuss.prepend($censor);

                    $lastTitle.children().first().text(config.censor.tagTitle);
                    $('.toctext', $tocTitle).text(config.censor.tagTitle);

                    // 有些用戶喜歡開小標題,一塊兒隱藏
                    $('ul', $tocTitle.parent()).remove();

                    // 經過審查之後可以按照正常討論進行處理了
                    badTitle = false;
                } else {
                    $tocTitle.parent().remove();
                }
            }

            // 摺疊討論內容
            if (!badTitle) {
                if ($discuss === null) {
                    $discuss = $('<div id="discuss_' + titleId + '"></div>');
                }

                visible[titleId] = !config.collapse;
                var arrow = config.collapse ? config.arrow.open : config.arrow.close;

                $lastTitle
                    .addClass('collapsable')
                    .css('cursor', 'pointer')
                    .prepend('<span style="color:' + config.arrow.color + ';" id="status_' + titleId + '">' + arrow + '</span>&nbsp;')
                    .after($discuss);

                if (config.collapse) {
                    $discuss.slideUp(config.duration);
                }

            } else {
                // 用戶要求直接隱藏黑名單裏的討論
                $lastTitle.remove();
                $discuss.remove();
            }

            $discuss = null;
        };

        $content.children().each(function (index, element) {
            if (element.tagName === titleLevel.toUpperCase()) {
                if ($discuss !== null) {
                    makeBox();
                }

                firstTopic = false;

                var titleElement = element.children[0];
                if (titleElement.className.indexOf('plainlinks') >= 0) {
                    titleElement = titleElement.children[0];
                }

                var titleId = titleElement.id;

                $lastTitle = $(element);
                $lastTitle.attr('data-id', titleId);
                $discuss = $('<div id="discuss_' + titleId + '"></div>');
            } else if ((titleLevel === 'h2' && element.tagName === 'H1') ||
                    (titleLevel === 'h3' && (element.tagName === 'H1' || element.tagName === 'H2')) ||
                    (element.tagName === 'DIV' && element.id === 'toc')) {
                // 官大一級壓死人
                if ($discuss !== null) {
                    makeBox();
                }
                $lastTitle = null;
                $discuss = null;
            } else if ((!firstTopic) && $discuss) {
                $discuss.append(element);
            }

            if (index === childrenCount - 1) {
                makeBox();

                $(titleLevel, '#mw-content-text').click(function (e) {
                    var id = $(this).attr('data-id');

                    // 點擊標題上的連結時不要摺疊(例如Wikiplus)
                    if ((!id) || e.target.tagName === 'A') {
                        return true;
                    }

                    if (visible[id] !== null) {
                        if (visible[id]) {
                            visible[id] = null;
                            $(document.getElementById('discuss_' + id)).slideUp(config.duration, function() {
                                visible[id] = false;
                                document.getElementById('status_' + id).innerHTML = config.arrow.open;
                            });
                        } else {
                            visible[id] = null;
                            $(document.getElementById('discuss_' + id)).slideDown(config.duration, function() {
                                visible[id] = true;
                                document.getElementById('status_' + id).innerHTML = config.arrow.close;
                            });
                        }
                    }
                });
            }
        });
    }
});