IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

ウェブページから NG ワードを消すグリースモンキー

id:otsune さんの RT 論を読んで

あと「公式RTだろうが非公式だろうが返信だろうが何でもいいけど、オレはタイムラインに◯◯の文字が見えるのが嫌なんだ」って要望は3年前から存在する。オレの持論は何十回も言ってるけど「すべてのブラウザはNGワード機能を搭載すべき」

@otsuneさんのRT論 - Togetter

なるほどー、たしかしー

というわけで

グリースモンキーを書いて見たよ!
こんな感じです

良かったら使ってください!

NG ワードのところに好きな単語を書き込むと、その単語を含む要素が消滅します!*1

// ==UserScript==
// @name           NG Filter!
// @namespace      http://d.hatena.ne.jp/amachang/
// @include        http://*
// ==/UserScript==

(function() {
    // NG ワードを書いてね☆
    var ngwords = <![CDATA[
        RT @
        QT @
        (via @
    ]]>.toString().split(/\n/).map(function(w) w.replace(/(^\s+)|(\s+$)/, '')).filter(function(w) w.length);

    // XPath を書ける人はこっちに XPath 式を書いてください
    var ngxpaths;/* = <![CDATA[
        ./text()[((contains(., 'RT') or contains(., 'QT')) and contains(., '@')) and (local-name(following-sibling::node()[1]) = 'a')]
        ./text()[(contains(., 'RT') or contains(., 'QT'))] and ./text()[contains(., '(via @') and (local-name(following-sibling::node()[1]) = 'a')]
    ]]>.toString().split(/\n/).map(function(w) w.replace(/(^\s+)|(\s+$)/, '')).filter(function(w) w.length);*/

    clean(document.body)
    document.addEventListener('DOMNodeInserted', function(e) { clean(e.target) }, false);
    document.addEventListener('DOMCharacterDataModified', function(e) { clean(e.target) }, false);
    document.addEventListener('DOMAttrModified', function(e) { if (e.target.tagName.match(/^(input|option)$/i)) clean(e.target) }, false);

    function clean(node) {
        switch (node.nodeType) {
        case 1:
            if (node.tagName.match(/^(input|option)$/i))
                node.value = '';
            else
                if (ngwords && ngwords.length) {
                    var xpath =
                            './/text()[' +
                                ngwords.map(function(w) 'contains(.,' + escapeXPath(w) + ')').join(' or ') +
                            '] | (.//input | .//option)[' +
                                ngwords.map(function(w) 'contains(@value,' + escapeXPath(w) + ')').join(' or ') +
                            ']';
                    var r = document.evaluate(xpath, node, null, 7, null);
                    for (var i = 0; i < r.snapshotLength; i ++) clean(r.snapshotItem(i));
                }

                if (ngxpaths && ngxpaths.length) {
                    xpath = '//*[' + ngxpaths.map(function(x) '(' + x + ')').join(' or ') + ']';
                    r = document.evaluate(xpath, node, null, 7, null);
                    for (var i = 0; i < r.snapshotLength; i ++) r.snapshotItem(i).textContent = '';
                }

            break;
        case 3:
            node.parentNode.textContent = '';
            break;
        }
    }

    function escapeXPath(text) {
        var matches = text.match(/[^"]+|"/g);

        function esc(t) {
            return t == '"' ? ('\'' + t + '\'') : ('"' + t + '"');
        }

        if (matches) {
            if (matches.length == 1) {
                return esc(matches[0]);
            }
            else {
                var results = [];
                for (var i = 0; i < matches.length; i ++) {
                    results.push(esc(matches[i]));
                }
                return 'concat(' + results.join(', ') + ')';
            }
        }
        else {
            return '""';
        }
    }
})();

ちなみに

僕は RT 大好きです。楽しいので。

*1:body にべったり(階層化せずに)キーワードが書いてあるような場合は、 body 全体が消滅します。それが困る場合は node.parentNode.textContent = ''; の部分を適宜 node.nodeValue = ''; とかに書き換えてみてください><