Greasy Fork

Greasy Fork is available in English.

彪扑

(从“烂虎扑屏蔽器”修改而来。)根据屏蔽标题和作者的关键词

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         彪扑
// @namespace    http://greasyfork.icu/gmail
// @version      0.3.2
// @description  (从“烂虎扑屏蔽器”修改而来。)根据屏蔽标题和作者的关键词
// @author       biopsy
// @license      Apache Licence 2.0
// @match        https://bbs.hupu.com/*
// @icon         https://w1.hoopchina.com.cn/images/pc/old/favicon.ico
// @grant        unSafeWindow
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_log
// @grant        GM_notification
// @grant        GM_listValues
// @grant        GM_deleteValue
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js
// ==/UserScript==

(function () {
    'use strict';
    const Keys = {blacklist: "banKeyword", tour: "firstTime"};

    function appendAssets() {
        const $head = $("head");
        $head.append($(`<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">`));
        $head.append($(`<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">`));
        $head.append($(`<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>`));
    }

    function appendElements() {
        // 添加右下角fab
        const $body = $("body");
        $body.append($(`<div class="fixed-action-btn">
  <a id="fab" class="btn-floating btn-large white">
    <i class="red-text large material-icons">lightbulb_outline</i>
  </a>
  <ul>
    <li id="fab-first"><a class="btn-floating white tooltip" data-tooltip="调试"><i class="red-text material-icons">mode_edit</i></a></li>
    <li id='showBanList'><a class="btn-floating white tooltip" data-tooltip="屏蔽词"><i class="red-text material-icons">reorder</i></a></li>
  </ul>
</div>
`));
        // 添加屏蔽词的chips
        $body.append($(`<div id="keywordList" class="card-panel teal lighten-1" style="background:grey;display:none; z-index:999;text-align:center;width: 66vw;height: 20vh;position: fixed;inset:0;margin: auto;">
    <div class="white-text">语法: %A% 开头屏蔽作者昵称, %I%开头屏蔽作者ID</div>
    <div class="chips chips-placeholder chips-initial"/>
    </div>`));
        // 添加个click事件,用于判断是否点击在chips组件之外
        $('#container').click(() => $('#keywordList').fadeOut(500));

        // 初始化 FAB
        M.FloatingActionButton.init(document.querySelectorAll('.fixed-action-btn'), {direction: 'left'});

        // 初始化 tooltip
        M.Tooltip.init(document.querySelectorAll('.tooltip'), {
            enterDelay: 200, exitDelay: 0, inDuration: 200, outDuration: 200, position: 'top', transitionMovement: 10
        });
    }


    // 添加新用户引导
    function addTour(count) {
        $('body').append($(`<div class="tap-target" data-target="fab">
    <div class="tap-target-content">
      <h5 style="color:white">自定义设置</h5><br/>
      <p style="color:white">在这里进行自定义配置</p><br/>
      <p style="color:white">此新用户引导只显示3次,这是第` + count + `次</p>
    </div>
  </div>`))

        // FeatureDiscovery初始化
        //$('.tap-target').tapTarget()
        let tapTargetElems = document.querySelectorAll('.tap-target')
        let tapTargetOptions = {
            open() {
                console.log('open')
            }, close() {
                console.log('close')
            }
        }
        M.TapTarget.init(tapTargetElems, tapTargetOptions)
        $('.tap-target').tapTarget('open')
    }


    // 显示屏蔽关键字列表(设置过多关键字会导致显示有点不和谐,待修复)
    function showBanKeyword() {
        // 挂载需要填入数据并设置可见性为true
        if ($('#keywordList').length > 0) {
            let elems = document.querySelectorAll('.chips')
            const words = GM_getValue(Keys.blacklist)
            const data = words.map(tag => ({tag}));
            let options = {
                data,
                placeholder: '添加一个屏蔽词',
                secondaryPlaceholder: '继续添加',
                onChipAdd: saveChipsData,
                onChipDelete: saveChipsData,
            }
            M.Chips.init(elems, options)
            alterChipsCardSize(data.length)
        }
    }

    // 根据chips数量来改变该card-panel的大小
    function alterChipsCardSize(length) {
        let height = ((length / 4 & 0) + 1) * 32 + 120;
        $('#keywordList').css({'display': 'inline', 'height': height + 'px'})
    }

    // 用于chips触发“添加”和“删除”事件后保存数据
    function saveChipsData() {
        let data = M.Chips.getInstance($('.chips')).chipsData
        let banKeyword = []
        data.forEach(function (e) {
            let keyword = e.tag
            banKeyword.push(keyword)
        })
        GM_log(banKeyword)
        alterChipsCardSize(banKeyword.length)
        // 将数据存储起来
        GM_setValue(Keys.blacklist, banKeyword)
    }


    //region filter

    /**
     * 语法: %A% %I%
     * @param {string}str
     * @return {(function(text:string): boolean)}
     */
    function createMatcher(str) {
        function createIndexMatcher(key) {
            return (text) => text.indexOf(key) > -1;
        }

        function createRegMatcher(key) {
            const pattern = new RegExp(key);
            return (text) => pattern.test(text);
        }

        if (str.startsWith('/') && s.endsWith('/')) {
            return createRegMatcher(str.substring(1, str.length - 1));
        } else {
            let fuzzy = false;
            if (str.indexOf('*') > -1) {
                fuzzy = true;
                str = str.replaceAll('*', '.*');
            }
            if (str.indexOf('?') > -1) {
                fuzzy = true;
                str = str.replaceAll('?', '.?');
            }
            return fuzzy ? createRegMatcher(str) : createIndexMatcher(str);
        }
    }

    /**
     *
     * @param {string[]} arr
     * @return {function(text:string): boolean}
     */
    function createFluxMatcher(arr) {
        const matchers = arr.map(x => createMatcher(x.trim()));
        return function (text) {
            if (!text) return false;
            for (let i = 0; i < matchers.length; i++) {
                if (matchers[i](text)) {
                    return true;
                }
            }
            return false;
        }
    }

    /**
     * @param {string[]} words
     * @return {{filterAuthor: boolean, author({id: string, name: string}): boolean, title(string): boolean}}
     */
    function createFilter(words = []) {
        const titles = [], ids = [], names = [];
        for (let i = 0; i < words.length; i++) {
            const s = words[i];
            if (s.length > 3) {
                const codes = [0, 1, 2].map(i => s.charCodeAt(i));
                if (codes[0] === 37 && codes[2] === 37) {
                    const c = codes[1];
                    if (c === 65 || c === 97) { // A
                        names.push(s.substring(3));
                    } else if (c === 73 || c === 105) { //I
                        ids.push(s.substring(3));
                    } else {
                        titles.push(s.substring(3));
                    }
                } else {
                    titles.push(s);
                }
            } else {
                titles.push(s);
            }
        }
        const titleMatcher = createFluxMatcher(titles);
        const idMatcher = createFluxMatcher(ids);
        const nameMatcher = createFluxMatcher(names);
        return {
            filterAuthor: ids.length > 0 || names.length > 0, title(text) {
                return titleMatcher(text);
            }, author({id, name}) {
                if (idMatcher(id)) return true;
                return nameMatcher(name);
            }
        }
    }

    function hidePostList(filter) {
        const articles = document.querySelectorAll('.bbs-sl-web-post li');

        function shouldHide(elem) {
            if (filter.filterAuthor) {
                const el = elem.querySelector('.post-auth a');
                const name = el.innerText;
                const id = el.getAttribute('href').split('/').at(-1);
                if (filter.author({id, name})) {
                    console.info('hide author:', name, id);
                    return true;
                }
            }
            const title = elem.querySelector('.post-title').innerText;
            if (filter.title(title)) {
                console.info('hide title:', title);
                return true;
            }
            return false;
        }

        for (let i = 0; i < articles.length; i++) {
            const foo = articles[i];
            if (shouldHide(foo)) {

                $(foo).css({'background-color': '#81c784', 'color': 'grey'}).fadeOut(500);
            }
        }
    }

    function hideAd() {
        // 屏蔽虎扑游戏
        $('.game-center-sidebar').remove()
        $('.game-center-entrance-container-title').remove()
        $('#game-center-entrance-container').remove()
        const $hotGame = $(':contains("热门游戏-即点即玩")');
        const el = $hotGame[$hotGame.length - 2]
        if (el) el.innerHTML = '';
    }

    //endregion

    appendAssets();
    appendElements();

    $(document).ready(function () {
        $('#showBanList').click(() => showBanKeyword());

        // 调试
        $('#fab-first').click(() => {
            const arr = GM_getValue(Keys.blacklist);
            console.info('黑名单', arr)
            const filter = createFilter();
            console.info('filter author', filter.filterAuthor)
            console.info(filter)
            console.info('author id', filter.author({id: '1234567', name: 'foobar'}))
            console.info('author name', filter.author({id: 'gg', name: '彭嘎'}))
            console.info('title contains', filter.title('战绩六芒星'))
            console.info('title *', filter.title('一起迎着上'))
            window.filter = filter;
        });
        // hideAd();
        const filter = createFilter(GM_getValue(Keys.blacklist));
        hidePostList(filter);
    })
})();