Merge branch 'filter'
This commit is contained in:
commit
1b8da8fd5f
124
4chan_x.user.js
124
4chan_x.user.js
@ -90,9 +90,8 @@
|
|||||||
},
|
},
|
||||||
Filtering: {
|
Filtering: {
|
||||||
'Anonymize': [false, 'Make everybody anonymous'],
|
'Anonymize': [false, 'Make everybody anonymous'],
|
||||||
'Filter': [false, 'Self-moderation placebo'],
|
'Filter': [true, 'Self-moderation placebo'],
|
||||||
'Filter OPs': [false, 'Filter OPs along with their threads'],
|
'Recursive Filtering': [true, 'Filter replies of filtered posts, recursively'],
|
||||||
'Recursive Filtering': [false, 'Filter replies of filtered posts, recursively'],
|
|
||||||
'Reply Hiding': [true, 'Hide single replies'],
|
'Reply Hiding': [true, 'Hide single replies'],
|
||||||
'Thread Hiding': [true, 'Hide entire threads'],
|
'Thread Hiding': [true, 'Hide entire threads'],
|
||||||
'Show Stubs': [true, 'Of hidden threads / replies']
|
'Show Stubs': [true, 'Of hidden threads / replies']
|
||||||
@ -532,86 +531,108 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
filter = {
|
filter = {
|
||||||
regexps: {},
|
filters: {},
|
||||||
callbacks: [],
|
|
||||||
init: function() {
|
init: function() {
|
||||||
var f, filter, key, m, _i, _len;
|
var boards, filter, hl, key, op, regexp, _i, _len, _ref, _ref2, _ref3;
|
||||||
for (key in config.filter) {
|
for (key in config.filter) {
|
||||||
if (!(m = conf[key].match(/^\/.+\/\w*$/gm))) continue;
|
this.filters[key] = [];
|
||||||
this.regexps[key] = [];
|
_ref = conf[key].split('\n');
|
||||||
for (_i = 0, _len = m.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
filter = m[_i];
|
filter = _ref[_i];
|
||||||
f = filter.match(/^\/(.+)\/(\w*)$/);
|
if (filter[0] === '#') continue;
|
||||||
|
if (!(regexp = filter.match(/\/(.+)\/(\w*)/))) continue;
|
||||||
|
filter = filter.replace(regexp[0], '');
|
||||||
|
boards = ((_ref2 = filter.match(/boards:([^;]+)/)) != null ? _ref2[1].toLowerCase() : void 0) || 'global';
|
||||||
|
if (boards !== 'global' && boards.split(',').indexOf(g.BOARD) === -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
this.regexps[key].push(RegExp(f[1], f[2]));
|
regexp = RegExp(regexp[1], regexp[2]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e.message);
|
alert(e.message);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
op = ((_ref3 = filter.match(/op:(yes|no|only)/)) != null ? _ref3[1].toLowerCase() : void 0) || 'no';
|
||||||
|
hl = /highlight/.test(filter);
|
||||||
|
this.filters[key].push(this.createFilter(regexp, op, hl));
|
||||||
}
|
}
|
||||||
this.callbacks.push(this[key]);
|
if (!this.filters[key].length) delete this.filters[key];
|
||||||
}
|
}
|
||||||
return g.callbacks.push(this.node);
|
if (Object.keys(this.filters).length) return g.callbacks.push(this.node);
|
||||||
|
},
|
||||||
|
createFilter: function(regexp, op, hl) {
|
||||||
|
return function(root, value, isOP) {
|
||||||
|
if (isOP && op === 'no' || op === 'only') return false;
|
||||||
|
if (!regexp.test(value)) return false;
|
||||||
|
if (hl) {
|
||||||
|
$.addClass(root, 'filter_highlight');
|
||||||
|
} else if (isOP) {
|
||||||
|
threadHiding.hideHide(root.parentNode);
|
||||||
|
} else {
|
||||||
|
replyHiding.hideHide(root.previousSibling);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
},
|
},
|
||||||
node: function(root) {
|
node: function(root) {
|
||||||
if (!root.className) {
|
var isOP, key, klass;
|
||||||
if (filter.callbacks.some(function(callback) {
|
klass = root.className;
|
||||||
return callback(root);
|
if (/\binlined\b/.test(klass)) return;
|
||||||
})) {
|
if (!(isOP = klass === 'op')) root = $('td[id]', root);
|
||||||
return replyHiding.hideHide($('td:not([nowrap])', root));
|
for (key in filter.filters) {
|
||||||
}
|
if (filter.test(root, key, isOP)) return;
|
||||||
} else if (root.className === 'op' && !g.REPLY && conf['Filter OPs']) {
|
|
||||||
if (filter.callbacks.some(function(callback) {
|
|
||||||
return callback(root);
|
|
||||||
})) {
|
|
||||||
return threadHiding.hideHide(root.parentNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
test: function(key, value) {
|
test: function(root, key, isOP) {
|
||||||
return filter.regexps[key].some(function(regexp) {
|
var filter, value, _i, _len, _ref;
|
||||||
return regexp.test(value);
|
value = this[key](root, isOP);
|
||||||
});
|
if (value === false) return false;
|
||||||
|
_ref = this.filters[key];
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
filter = _ref[_i];
|
||||||
|
if (filter(root, value, isOP)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
name: function(root) {
|
name: function(root, isOP) {
|
||||||
var name;
|
var name;
|
||||||
name = root.className === 'op' ? $('.postername', root) : $('.commentpostername', root);
|
name = isOP ? $('.postername', root) : $('.commentpostername', root);
|
||||||
return filter.test('name', name.textContent);
|
return name.textContent;
|
||||||
},
|
},
|
||||||
tripcode: function(root) {
|
tripcode: function(root) {
|
||||||
var trip;
|
var trip;
|
||||||
if (trip = $('.postertrip', root)) {
|
if (trip = $('.postertrip', root)) return trip.textContent;
|
||||||
return filter.test('tripcode', trip.textContent);
|
return false;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
email: function(root) {
|
email: function(root) {
|
||||||
var mail;
|
var mail;
|
||||||
if (mail = $('.linkmail', root)) return filter.test('email', mail.href);
|
if (!(mail = $('.linkmail', root))) return mail.href;
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
subject: function(root) {
|
subject: function(root, isOP) {
|
||||||
var sub;
|
var sub;
|
||||||
sub = root.className === 'op' ? $('.filetitle', root) : $('.replytitle', root);
|
sub = isOP ? $('.filetitle', root) : $('.replytitle', root);
|
||||||
return filter.test('subject', sub.textContent);
|
return sub.textContent;
|
||||||
},
|
},
|
||||||
comment: function(root) {
|
comment: function(root) {
|
||||||
return filter.test('comment', ($.el('a', {
|
return ($.el('a', {
|
||||||
innerHTML: $('blockquote', root).innerHTML.replace(/<br>/g, '\n')
|
innerHTML: $('blockquote', root).innerHTML.replace(/<br>/g, '\n')
|
||||||
})).textContent);
|
})).textContent;
|
||||||
},
|
},
|
||||||
filename: function(root) {
|
filename: function(root) {
|
||||||
var file;
|
var file;
|
||||||
if (file = $('.filesize span', root)) {
|
if (file = $('.filesize > span', root)) return file.title;
|
||||||
return filter.test('filename', file.title);
|
return false;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
filesize: function(root) {
|
filesize: function(root) {
|
||||||
var img;
|
var img;
|
||||||
if (img = $('img[md5]', root)) return filter.test('filesize', img.alt);
|
if (img = $('img[md5]', root)) return img.alt;
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
md5: function(root) {
|
md5: function(root) {
|
||||||
var img;
|
var img;
|
||||||
if (img = $('img[md5]', root)) {
|
if (img = $('img[md5]', root)) return img.getAttribute('md5');
|
||||||
return filter.test('md5', img.getAttribute('md5'));
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4032,13 +4053,16 @@ img[md5], img[md5] + img {\
|
|||||||
.inlined {\
|
.inlined {\
|
||||||
opacity: .5;\
|
opacity: .5;\
|
||||||
}\
|
}\
|
||||||
.inline td.reply {\
|
.inline .reply {\
|
||||||
background-color: rgba(255, 255, 255, 0.15);\
|
background-color: rgba(255, 255, 255, 0.15);\
|
||||||
border: 1px solid rgba(128, 128, 128, 0.5);\
|
border: 1px solid rgba(128, 128, 128, 0.5);\
|
||||||
}\
|
}\
|
||||||
.filetitle, .replytitle, .postername, .commentpostername, .postertrip {\
|
.filetitle, .replytitle, .postername, .commentpostername, .postertrip {\
|
||||||
background: none;\
|
background: none;\
|
||||||
}\
|
}\
|
||||||
|
.filter_highlight {\
|
||||||
|
box-shadow: -5px 0 rgba(255,0,0,0.5);\
|
||||||
|
}\
|
||||||
.filtered {\
|
.filtered {\
|
||||||
text-decoration: line-through;\
|
text-decoration: line-through;\
|
||||||
}\
|
}\
|
||||||
|
|||||||
137
script.coffee
137
script.coffee
@ -13,9 +13,8 @@ config =
|
|||||||
'Check for Updates': [true, 'Check for updated versions of 4chan X']
|
'Check for Updates': [true, 'Check for updated versions of 4chan X']
|
||||||
Filtering:
|
Filtering:
|
||||||
'Anonymize': [false, 'Make everybody anonymous']
|
'Anonymize': [false, 'Make everybody anonymous']
|
||||||
'Filter': [false, 'Self-moderation placebo']
|
'Filter': [true, 'Self-moderation placebo']
|
||||||
'Filter OPs': [false, 'Filter OPs along with their threads']
|
'Recursive Filtering': [true, 'Filter replies of filtered posts, recursively']
|
||||||
'Recursive Filtering': [false, 'Filter replies of filtered posts, recursively']
|
|
||||||
'Reply Hiding': [true, 'Hide single replies']
|
'Reply Hiding': [true, 'Hide single replies']
|
||||||
'Thread Hiding': [true, 'Hide entire threads']
|
'Thread Hiding': [true, 'Hide entire threads']
|
||||||
'Show Stubs': [true, 'Of hidden threads / replies']
|
'Show Stubs': [true, 'Of hidden threads / replies']
|
||||||
@ -408,58 +407,113 @@ $$ = (selector, root=d.body) ->
|
|||||||
Array::slice.call root.querySelectorAll selector
|
Array::slice.call root.querySelectorAll selector
|
||||||
|
|
||||||
filter =
|
filter =
|
||||||
regexps: {}
|
filters: {}
|
||||||
callbacks: []
|
|
||||||
init: ->
|
init: ->
|
||||||
for key of config.filter
|
for key of config.filter
|
||||||
unless m = conf[key].match /^\/.+\/\w*$/gm
|
@filters[key] = []
|
||||||
continue
|
for filter in conf[key].split('\n')
|
||||||
@regexps[key] = []
|
continue if filter[0] is '#'
|
||||||
for filter in m
|
|
||||||
f = filter.match /^\/(.+)\/(\w*)$/
|
|
||||||
try
|
|
||||||
@regexps[key].push RegExp f[1], f[2]
|
|
||||||
catch e
|
|
||||||
alert e.message
|
|
||||||
#only execute what's filterable
|
|
||||||
@callbacks.push @[key]
|
|
||||||
|
|
||||||
g.callbacks.push @node
|
unless regexp = filter.match /\/(.+)\/(\w*)/
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Don't mix up filter flags with the regular expression.
|
||||||
|
filter = filter.replace regexp[0], ''
|
||||||
|
|
||||||
|
# Do not add this filter to the list if it's not a global one
|
||||||
|
# and it's not specifically applicable to the current board.
|
||||||
|
# Defaults to global.
|
||||||
|
boards = filter.match(/boards:([^;]+)/)?[1].toLowerCase() or 'global'
|
||||||
|
if boards isnt 'global' and boards.split(',').indexOf(g.BOARD) is -1
|
||||||
|
continue
|
||||||
|
|
||||||
|
try
|
||||||
|
# Please, don't write silly regular expressions.
|
||||||
|
regexp = RegExp regexp[1], regexp[2]
|
||||||
|
catch e
|
||||||
|
# I warned you, bro.
|
||||||
|
alert e.message
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Filter OPs along with their threads, replies only, or both.
|
||||||
|
# Defaults to replies only.
|
||||||
|
op = filter.match(/op:(yes|no|only)/)?[1].toLowerCase() or 'no'
|
||||||
|
|
||||||
|
# Highlight the post, or hide it.
|
||||||
|
# Defaults to post hiding.
|
||||||
|
hl = /highlight/.test filter
|
||||||
|
|
||||||
|
@filters[key].push @createFilter regexp, op, hl
|
||||||
|
|
||||||
|
# Only execute filter types that contain valid filters.
|
||||||
|
unless @filters[key].length
|
||||||
|
delete @filters[key]
|
||||||
|
|
||||||
|
if Object.keys(@filters).length
|
||||||
|
g.callbacks.push @node
|
||||||
|
|
||||||
|
createFilter: (regexp, op, hl) ->
|
||||||
|
(root, value, isOP) ->
|
||||||
|
if isOP and op is 'no' or op is 'only'
|
||||||
|
return false
|
||||||
|
unless regexp.test value
|
||||||
|
return false
|
||||||
|
if hl
|
||||||
|
$.addClass root, 'filter_highlight'
|
||||||
|
else if isOP
|
||||||
|
threadHiding.hideHide root.parentNode
|
||||||
|
else
|
||||||
|
replyHiding.hideHide root.previousSibling
|
||||||
|
true
|
||||||
|
|
||||||
node: (root) ->
|
node: (root) ->
|
||||||
unless root.className
|
klass = root.className
|
||||||
if filter.callbacks.some((callback) -> callback root)
|
return if /\binlined\b/.test klass
|
||||||
replyHiding.hideHide $ 'td:not([nowrap])', root
|
unless isOP = klass is 'op'
|
||||||
else if root.className is 'op' and not g.REPLY and conf['Filter OPs']
|
root = $ 'td[id]', root
|
||||||
if filter.callbacks.some((callback) -> callback root)
|
for key of filter.filters
|
||||||
threadHiding.hideHide root.parentNode
|
if filter.test root, key, isOP
|
||||||
|
return
|
||||||
|
|
||||||
test: (key, value) ->
|
test: (root, key, isOP) ->
|
||||||
filter.regexps[key].some (regexp) -> regexp.test value
|
value = @[key] root, isOP
|
||||||
|
if value is false
|
||||||
|
# Return if there's nothing to filter (no tripcode for example).
|
||||||
|
return false
|
||||||
|
|
||||||
name: (root) ->
|
for filter in @filters[key]
|
||||||
name = if root.className is 'op' then $ '.postername', root else $ '.commentpostername', root
|
if filter root, value, isOP
|
||||||
filter.test 'name', name.textContent
|
return true
|
||||||
|
false
|
||||||
|
|
||||||
|
name: (root, isOP) ->
|
||||||
|
name = if isOP then $ '.postername', root else $ '.commentpostername', root
|
||||||
|
name.textContent
|
||||||
tripcode: (root) ->
|
tripcode: (root) ->
|
||||||
if trip = $ '.postertrip', root
|
if trip = $ '.postertrip', root
|
||||||
filter.test 'tripcode', trip.textContent
|
return trip.textContent
|
||||||
|
false
|
||||||
email: (root) ->
|
email: (root) ->
|
||||||
if mail = $ '.linkmail', root
|
unless mail = $ '.linkmail', root
|
||||||
filter.test 'email', mail.href
|
return mail.href
|
||||||
subject: (root) ->
|
false
|
||||||
sub = if root.className is 'op' then $ '.filetitle', root else $ '.replytitle', root
|
subject: (root, isOP) ->
|
||||||
filter.test 'subject', sub.textContent
|
sub = if isOP then $ '.filetitle', root else $ '.replytitle', root
|
||||||
|
sub.textContent
|
||||||
comment: (root) ->
|
comment: (root) ->
|
||||||
filter.test 'comment', ($.el 'a', innerHTML: $('blockquote', root).innerHTML.replace /<br>/g, '\n').textContent
|
($.el 'a', innerHTML: $('blockquote', root).innerHTML.replace /<br>/g, '\n').textContent
|
||||||
filename: (root) ->
|
filename: (root) ->
|
||||||
if file = $ '.filesize span', root
|
if file = $ '.filesize > span', root
|
||||||
filter.test 'filename', file.title
|
return file.title
|
||||||
|
false
|
||||||
filesize: (root) ->
|
filesize: (root) ->
|
||||||
if img = $ 'img[md5]', root
|
if img = $ 'img[md5]', root
|
||||||
filter.test 'filesize', img.alt
|
return img.alt
|
||||||
|
false
|
||||||
md5: (root) ->
|
md5: (root) ->
|
||||||
if img = $ 'img[md5]', root
|
if img = $ 'img[md5]', root
|
||||||
filter.test 'md5', img.getAttribute('md5')
|
return img.getAttribute 'md5'
|
||||||
|
false
|
||||||
|
|
||||||
strikethroughQuotes =
|
strikethroughQuotes =
|
||||||
init: ->
|
init: ->
|
||||||
@ -3306,13 +3360,16 @@ img[md5], img[md5] + img {
|
|||||||
.inlined {
|
.inlined {
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
}
|
}
|
||||||
.inline td.reply {
|
.inline .reply {
|
||||||
background-color: rgba(255, 255, 255, 0.15);
|
background-color: rgba(255, 255, 255, 0.15);
|
||||||
border: 1px solid rgba(128, 128, 128, 0.5);
|
border: 1px solid rgba(128, 128, 128, 0.5);
|
||||||
}
|
}
|
||||||
.filetitle, .replytitle, .postername, .commentpostername, .postertrip {
|
.filetitle, .replytitle, .postername, .commentpostername, .postertrip {
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
.filter_highlight {
|
||||||
|
box-shadow: -5px 0 rgba(255,0,0,0.5);
|
||||||
|
}
|
||||||
.filtered {
|
.filtered {
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user