greasemonkey script for thread filtering (based on user or subject) for kawf-based forums
1// $Id: kawfilter.user.js,v 1.4 2009/04/06 19:30:19 jcs Exp $
2//
3// ==UserScript==
4// @name kawfilter
5// @namespace http://jcs.org/code/
6// @description hide posts and threads made by certain users, or matching certain subjects
7// @include http://forums.wayot.org/other/
8// @include http://forums.wayot.org/other/pages/*
9// @include http://f.wayot.org/other/
10// @include http://f.wayot.org/other/pages/*
11// @author joshua stein <jcs@jcs.org>
12// ==/UserScript==
13//
14// Copyright (c) 2008-2009 joshua stein <jcs@jcs.org>
15//
16// Redistribution and use in source and binary forms, with or without
17// modification, are permitted provided that the following conditions
18// are met:
19//
20// 1. Redistributions of source code must retain the above copyright
21// notice, this list of conditions and the following disclaimer.
22// 2. Redistributions in binary form must reproduce the above copyright
23// notice, this list of conditions and the following disclaimer in the
24// documentation and/or other materials provided with the distribution.
25// 3. The name of the author may not be used to endorse or promote products
26// derived from this software without specific prior written permission.
27//
28// THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
29// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
30// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
31// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
32// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
33// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
37// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38//
39
40// both of these searches are case insensitive and may contain regexp wildcards
41// so be sure to escape regexp metachars (such as \*+?|{[()]^$.) with a
42// backslash, but since they are in quotes, you have to escape the backslash
43//
44// "username (blah)" has to be "username \\(blah\\)"
45// or just use single quotes for such things and write 'username \(blah\)'
46
47// usernames to filter, must match entire username (case insensitive)
48var usernames = [ "username 1", "troll.*" ];
49
50// posts to filter by subject, only needs to match start of subject line
51var subjects = [ "meal post" ];
52
53
54// make this false to show replies under hidden posts
55var hide_replies = true;
56
57// make this true to completely kill matching lines instead of collapsing
58var hide_matches = false;
59
60
61var head = document.getElementsByTagName("head")[0];
62var style = document.createElement("style");
63style.type = "text/css";
64style.innerHTML = ".filtered a { font-size: smaller; color: gray; " +
65 "text-decoration: none; }";
66head.appendChild(style);
67
68var ureg = new RegExp('href="([^"]+)".* (<b>(' + usernames.join('|')
69 + ')<\/b>.*)', 'i');
70var sreg = new RegExp('href="([^"]+)">(' + subjects.join('|') +
71 ').* <b>(.+)<\/b>(.*)', 'i');
72
73var lis = document.evaluate("//li", document.body, null,
74 XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
75for (var i = 0; i < lis.snapshotLength; i++) {
76 a = lis.snapshotItem(i);
77
78 match = false;
79 if (usernames.length > 0 && (m = a.innerHTML.match(ureg))) {
80 a.innerHTML = "<span class=\"filtered\"><a href=\"" + m[1] +
81 "\">post by filtered user " + m[2].replace(/<\/?b>/, "") +
82 "</a></span>";
83
84 match = true;
85 } else if (subjects.length > 0 && (m = a.innerHTML.match(sreg))) {
86 a.innerHTML = "<span class=\"filtered\"><a href=\"" + m[1] +
87 "\">post by " + m[2].replace(/<\/?b>/, "") + " with " +
88 "filtered subject '" + subjects[j] + "' " + m[3] +
89 "</a></span>";
90
91 match = true;
92 }
93
94 if (match) {
95 if (hide_replies && a.parentNode)
96 for (var c = 0; c < a.parentNode.childNodes.length; c++)
97 if (a.parentNode.childNodes[c] == a)
98 /* next node should be text, then a <ul> for the reply */
99 for (var ch = c + 2; ch < a.parentNode.childNodes.length;
100 ch += 2)
101 if (z = a.parentNode.childNodes[ch])
102 z.style.display = "none";
103
104 if (hide_matches) {
105 a.style.display = "none";
106 continue;
107 }
108 }
109}