Add Comment Expansion.
Fix a bug in Quote Backlinks.
This commit is contained in:
parent
acc359ca30
commit
b8db488591
190
4chan_x.user.js
190
4chan_x.user.js
@ -43,7 +43,7 @@
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var $, $$, Anonymize, ArchiveLink, AutoGIF, Board, Build, Clone, Conf, Config, DeleteLink, DownloadLink, Favicon, FileInfo, Filter, Get, Header, ImageExpand, ImageHover, Main, Menu, Notification, Polyfill, Post, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Recursive, Redirect, RelativeDates, ReplyHiding, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, Time, UI, Unread, d, doc, g,
|
||||
var $, $$, Anonymize, ArchiveLink, AutoGIF, Board, Build, Clone, Conf, Config, DeleteLink, DownloadLink, ExpandComment, Favicon, FileInfo, Filter, Get, Header, ImageExpand, ImageHover, Main, Menu, Notification, Polyfill, Post, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Recursive, Redirect, RelativeDates, ReplyHiding, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, Time, UI, Unread, d, doc, g,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
||||
@ -2653,7 +2653,7 @@
|
||||
});
|
||||
} else {
|
||||
$.addClass(root, 'warning');
|
||||
root.textContent = status === 404 ? "Thread No." + threadID + " 404'd." : "Error " + req.status + ": " + req.statusText + ".";
|
||||
root.textContent = status === 404 ? "Thread No." + threadID + " 404'd." : "Error " + req.statusText + " (" + req.status + ").";
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -3069,7 +3069,7 @@
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
quote = _ref[_i];
|
||||
containers = [QuoteBacklink.getContainer(quote)];
|
||||
if (post = g.posts[quote]) {
|
||||
if ((post = g.posts[quote]) && post.nodes.backlinkContainer) {
|
||||
_ref1 = post.clones;
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
clone = _ref1[_j];
|
||||
@ -3091,11 +3091,11 @@
|
||||
},
|
||||
secondNode: function() {
|
||||
var container;
|
||||
if (this.isClone && this.origin.nodes.backlinkContainer) {
|
||||
if (this.isClone && (this.origin.isReply || Conf['OP Backlinks'])) {
|
||||
this.nodes.backlinkContainer = $('.container', this.nodes.info);
|
||||
return;
|
||||
}
|
||||
if (!(Conf['OP Backlinks'] || this.isReply)) {
|
||||
if (!(this.isReply || Conf['OP Backlinks'])) {
|
||||
return;
|
||||
}
|
||||
container = QuoteBacklink.getContainer(this.fullID);
|
||||
@ -3891,6 +3891,86 @@
|
||||
}
|
||||
};
|
||||
|
||||
ExpandComment = {
|
||||
init: function() {
|
||||
if (g.VIEW !== 'index' || !Conf['Comment Expansion']) {
|
||||
return;
|
||||
}
|
||||
return Post.prototype.callbacks.push({
|
||||
name: 'Comment Expansion',
|
||||
cb: this.node
|
||||
});
|
||||
},
|
||||
node: function() {
|
||||
var a;
|
||||
if (a = $('.abbr > a', this.nodes.comment)) {
|
||||
return $.on(a, 'click', ExpandComment.expand);
|
||||
}
|
||||
},
|
||||
expand: function(e) {
|
||||
var a, post;
|
||||
e.preventDefault();
|
||||
post = Get.postFromNode(this);
|
||||
this.textContent = "Post No." + post + " Loading...";
|
||||
a = this;
|
||||
return $.cache("//api.4chan.org" + this.pathname + ".json", function() {
|
||||
return ExpandComment.parse(this, a, post);
|
||||
});
|
||||
},
|
||||
parse: function(req, a, post) {
|
||||
var comment, href, postObj, posts, prev, quote, spoilerRange, _i, _j, _len, _len1, _ref;
|
||||
if (req.status !== 200) {
|
||||
a.textContent = "Error " + req.statusText + " (" + req.status + ")";
|
||||
return;
|
||||
}
|
||||
posts = JSON.parse(req.response).posts;
|
||||
if (spoilerRange = posts[0].custom_spoiler) {
|
||||
Build.spoilerRange[g.BOARD] = spoilerRange;
|
||||
}
|
||||
for (_i = 0, _len = posts.length; _i < _len; _i++) {
|
||||
postObj = posts[_i];
|
||||
if (postObj.no === post.ID) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (postObj.no !== post.ID) {
|
||||
a.textContent = "Post No." + post + " not found.";
|
||||
return;
|
||||
}
|
||||
comment = post.nodes.comment;
|
||||
comment.innerHTML = postObj.com;
|
||||
_ref = $$('.quotelink', comment);
|
||||
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
|
||||
quote = _ref[_j];
|
||||
href = quote.getAttribute('href');
|
||||
if (href[0] === '/') {
|
||||
continue;
|
||||
}
|
||||
quote.href = "/" + post.board + "/res/" + href;
|
||||
}
|
||||
post.parseComment();
|
||||
post.parseQuotes();
|
||||
if (Conf['Resurrect Quotes']) {
|
||||
Quotify.node.call(post);
|
||||
}
|
||||
if (Conf['Quote Preview']) {
|
||||
QuotePreview.node.call(post);
|
||||
}
|
||||
if (Conf['Quote Inline']) {
|
||||
QuoteInline.node.call(post);
|
||||
}
|
||||
if (Conf['Mark OP Quotes']) {
|
||||
QuoteOP.node.call(post);
|
||||
}
|
||||
if (Conf['Mark Cross-thread Quotes']) {
|
||||
QuoteCT.node.call(post);
|
||||
}
|
||||
prev = comment.previousSibling;
|
||||
$.rm(comment);
|
||||
return $.after(prev, comment);
|
||||
}
|
||||
};
|
||||
|
||||
ThreadExcerpt = {
|
||||
init: function() {
|
||||
if (g.VIEW !== 'thread' || !Conf['Thread Excerpt']) {
|
||||
@ -5373,7 +5453,7 @@
|
||||
};
|
||||
|
||||
function Post(root, thread, board, that) {
|
||||
var alt, anchor, bq, capcode, data, date, email, file, fileInfo, flag, hash, i, info, name, node, nodes, pathname, post, quotelink, quotes, size, subject, text, thumb, tripcode, uniqueID, unit, _i, _j, _k, _len, _len1, _ref, _ref1, _ref2;
|
||||
var alt, anchor, capcode, date, email, file, fileInfo, flag, info, name, post, size, subject, thumb, tripcode, uniqueID, unit;
|
||||
this.thread = thread;
|
||||
this.board = board;
|
||||
if (that == null) {
|
||||
@ -5424,40 +5504,8 @@
|
||||
this.nodes.date = date;
|
||||
this.info.date = new Date(date.dataset.utc * 1000);
|
||||
}
|
||||
bq = this.nodes.comment.cloneNode(true);
|
||||
_ref = $$('.abbr, .capcodeReplies, .exif, b', bq);
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
node = _ref[_i];
|
||||
$.rm(node);
|
||||
}
|
||||
text = [];
|
||||
nodes = d.evaluate('.//br|.//text()', bq, null, 7, null);
|
||||
for (i = _j = 0, _ref1 = nodes.snapshotLength; 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
|
||||
text.push((data = nodes.snapshotItem(i).data) ? data : '\n');
|
||||
}
|
||||
this.info.comment = text.join('').trim().replace(/\s+$/gm, '');
|
||||
quotes = {};
|
||||
_ref2 = $$('.quotelink', this.nodes.comment);
|
||||
for (_k = 0, _len1 = _ref2.length; _k < _len1; _k++) {
|
||||
quotelink = _ref2[_k];
|
||||
hash = quotelink.hash;
|
||||
if (!hash) {
|
||||
continue;
|
||||
}
|
||||
pathname = quotelink.pathname;
|
||||
if (/catalog$/.test(pathname)) {
|
||||
continue;
|
||||
}
|
||||
if (quotelink.hostname !== 'boards.4chan.org') {
|
||||
continue;
|
||||
}
|
||||
this.nodes.quotelinks.push(quotelink);
|
||||
if (quotelink.parentNode.parentNode.className === 'capcodeReplies') {
|
||||
continue;
|
||||
}
|
||||
quotes["" + (pathname.split('/')[1]) + "." + hash.slice(2)] = true;
|
||||
}
|
||||
this.quotes = Object.keys(quotes);
|
||||
this.parseComment();
|
||||
this.parseQuotes();
|
||||
if ((file = $('.file', post)) && (thumb = $('img[data-md5]', file))) {
|
||||
alt = thumb.alt;
|
||||
anchor = thumb.parentNode;
|
||||
@ -5491,6 +5539,51 @@
|
||||
}
|
||||
}
|
||||
|
||||
Post.prototype.parseComment = function() {
|
||||
var bq, data, i, node, nodes, text, _i, _j, _len, _ref, _ref1;
|
||||
bq = this.nodes.comment.cloneNode(true);
|
||||
_ref = $$('.abbr, .capcodeReplies, .exif, b', bq);
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
node = _ref[_i];
|
||||
$.rm(node);
|
||||
}
|
||||
text = [];
|
||||
nodes = d.evaluate('.//br|.//text()', bq, null, 7, null);
|
||||
for (i = _j = 0, _ref1 = nodes.snapshotLength; 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
|
||||
text.push((data = nodes.snapshotItem(i).data) ? data : '\n');
|
||||
}
|
||||
return this.info.comment = text.join('').trim().replace(/\s+$/gm, '');
|
||||
};
|
||||
|
||||
Post.prototype.parseQuotes = function() {
|
||||
var hash, pathname, quotelink, quotes, _i, _len, _ref;
|
||||
quotes = {};
|
||||
_ref = $$('.quotelink', this.nodes.comment);
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
quotelink = _ref[_i];
|
||||
hash = quotelink.hash;
|
||||
if (!hash) {
|
||||
continue;
|
||||
}
|
||||
pathname = quotelink.pathname;
|
||||
if (/catalog$/.test(pathname)) {
|
||||
continue;
|
||||
}
|
||||
if (quotelink.hostname !== 'boards.4chan.org') {
|
||||
continue;
|
||||
}
|
||||
this.nodes.quotelinks.push(quotelink);
|
||||
if (quotelink.parentNode.parentNode.className === 'capcodeReplies') {
|
||||
continue;
|
||||
}
|
||||
quotes["" + (pathname.split('/')[1]) + "." + hash.slice(2)] = true;
|
||||
}
|
||||
if (this.isClone) {
|
||||
return;
|
||||
}
|
||||
return this.quotes = Object.keys(quotes);
|
||||
};
|
||||
|
||||
Post.prototype.kill = function(file, now) {
|
||||
var clone, quotelink, strong, _i, _j, _len, _len1, _ref, _ref1;
|
||||
now || (now = new Date());
|
||||
@ -5556,7 +5649,7 @@
|
||||
__extends(Clone, _super);
|
||||
|
||||
function Clone(origin, context) {
|
||||
var file, index, info, inline, inlined, key, nodes, post, quotelink, root, val, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3, _ref4;
|
||||
var file, index, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3;
|
||||
this.origin = origin;
|
||||
this.context = context;
|
||||
_ref = ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply'];
|
||||
@ -5612,18 +5705,12 @@
|
||||
if (nodes.date) {
|
||||
this.nodes.date = $('.dateTime', info);
|
||||
}
|
||||
_ref3 = $$('.quotelink', this.nodes.comment);
|
||||
for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
|
||||
quotelink = _ref3[_l];
|
||||
if (quotelink.hash || $.hasClass(quotelink, 'deadlink')) {
|
||||
this.nodes.quotelinks.push(quotelink);
|
||||
}
|
||||
}
|
||||
this.parseQuotes();
|
||||
if (origin.file) {
|
||||
this.file = {};
|
||||
_ref4 = origin.file;
|
||||
for (key in _ref4) {
|
||||
val = _ref4[key];
|
||||
_ref3 = origin.file;
|
||||
for (key in _ref3) {
|
||||
val = _ref3[key];
|
||||
this.file[key] = val;
|
||||
}
|
||||
file = $('.file', post);
|
||||
@ -5739,6 +5826,7 @@
|
||||
initFeature('Reveal Spoilers', RevealSpoilers);
|
||||
initFeature('Auto-GIF', AutoGIF);
|
||||
initFeature('Image Hover', ImageHover);
|
||||
initFeature('Comment Expansion', ExpandComment);
|
||||
initFeature('Thread Excerpt', ThreadExcerpt);
|
||||
initFeature('Favicon', Favicon);
|
||||
initFeature('Unread', Unread);
|
||||
|
||||
@ -1469,7 +1469,7 @@ Get =
|
||||
if status is 404
|
||||
"Thread No.#{threadID} 404'd."
|
||||
else
|
||||
"Error #{req.status}: #{req.statusText}."
|
||||
"Error #{req.statusText} (#{req.status})."
|
||||
return
|
||||
|
||||
posts = JSON.parse(req.response).posts
|
||||
@ -1839,7 +1839,9 @@ QuoteBacklink =
|
||||
textContent: QuoteBacklink.funk @ID
|
||||
for quote in @quotes
|
||||
containers = [QuoteBacklink.getContainer quote]
|
||||
if post = g.posts[quote]
|
||||
if (post = g.posts[quote]) and post.nodes.backlinkContainer
|
||||
# Don't add OP clones when OP Backlinks is disabled,
|
||||
# as the clones won't have the backlink containers.
|
||||
for clone in post.clones
|
||||
containers.push clone.nodes.backlinkContainer
|
||||
for container in containers
|
||||
@ -1851,11 +1853,11 @@ QuoteBacklink =
|
||||
$.add container, [$.tn(' '), link]
|
||||
return
|
||||
secondNode: ->
|
||||
if @isClone and @origin.nodes.backlinkContainer
|
||||
if @isClone and (@origin.isReply or Conf['OP Backlinks'])
|
||||
@nodes.backlinkContainer = $ '.container', @nodes.info
|
||||
return
|
||||
# Don't backlink the OP.
|
||||
return unless Conf['OP Backlinks'] or @isReply
|
||||
return unless @isReply or Conf['OP Backlinks']
|
||||
container = QuoteBacklink.getContainer @fullID
|
||||
@nodes.backlinkContainer = container
|
||||
$.add @nodes.info, container
|
||||
@ -2420,6 +2422,62 @@ ImageHover =
|
||||
$.ajax URL, onreadystatechange: (-> clearTimeout timeoutID if @status is 404),
|
||||
type: 'head'
|
||||
|
||||
ExpandComment =
|
||||
init: ->
|
||||
return if g.VIEW isnt 'index' or !Conf['Comment Expansion']
|
||||
|
||||
Post::callbacks.push
|
||||
name: 'Comment Expansion'
|
||||
cb: @node
|
||||
node: ->
|
||||
if a = $ '.abbr > a', @nodes.comment
|
||||
$.on a, 'click', ExpandComment.expand
|
||||
expand: (e) ->
|
||||
e.preventDefault()
|
||||
post = Get.postFromNode @
|
||||
@textContent = "Post No.#{post} Loading..."
|
||||
a = @
|
||||
$.cache "//api.4chan.org#{@pathname}.json", -> ExpandComment.parse @, a, post
|
||||
parse: (req, a, post) ->
|
||||
if req.status isnt 200
|
||||
a.textContent = "Error #{req.statusText} (#{req.status})"
|
||||
return
|
||||
|
||||
posts = JSON.parse(req.response).posts
|
||||
if spoilerRange = posts[0].custom_spoiler
|
||||
Build.spoilerRange[g.BOARD] = spoilerRange
|
||||
|
||||
for postObj in posts
|
||||
break if postObj.no is post.ID
|
||||
if postObj.no isnt post.ID
|
||||
a.textContent = "Post No.#{post} not found."
|
||||
return
|
||||
|
||||
{comment} = post.nodes
|
||||
comment.innerHTML = postObj.com
|
||||
for quote in $$ '.quotelink', comment
|
||||
href = quote.getAttribute 'href'
|
||||
continue if href[0] is '/' # Cross-board quote, or board link
|
||||
quote.href = "/#{post.board}/res/#{href}" # Fix pathnames
|
||||
post.parseComment()
|
||||
post.parseQuotes()
|
||||
if Conf['Resurrect Quotes']
|
||||
Quotify.node.call post
|
||||
if Conf['Quote Preview']
|
||||
QuotePreview.node.call post
|
||||
if Conf['Quote Inline']
|
||||
QuoteInline.node.call post
|
||||
if Conf['Mark OP Quotes']
|
||||
QuoteOP.node.call post
|
||||
if Conf['Mark Cross-thread Quotes']
|
||||
QuoteCT.node.call post
|
||||
# XXX g code
|
||||
# XXX sci math
|
||||
# Fix linkifiers:
|
||||
prev = comment.previousSibling
|
||||
$.rm comment
|
||||
$.after prev, comment
|
||||
|
||||
ThreadExcerpt =
|
||||
init: ->
|
||||
return if g.VIEW isnt 'thread' or !Conf['Thread Excerpt']
|
||||
|
||||
@ -66,47 +66,8 @@ class Post
|
||||
@nodes.date = date
|
||||
@info.date = new Date date.dataset.utc * 1000
|
||||
|
||||
# Get the comment's text.
|
||||
# <br> -> \n
|
||||
# Remove:
|
||||
# 'Comment too long'...
|
||||
# Admin/Mod/Dev replies. (/q/)
|
||||
# EXIF data. (/p/)
|
||||
# Rolls. (/tg/)
|
||||
# Preceding and following new lines.
|
||||
# Trailing spaces.
|
||||
bq = @nodes.comment.cloneNode true
|
||||
for node in $$ '.abbr, .capcodeReplies, .exif, b', bq
|
||||
$.rm node
|
||||
text = []
|
||||
# XPathResult.ORDERED_NODE_SNAPSHOT_TYPE === 7
|
||||
nodes = d.evaluate './/br|.//text()', bq, null, 7, null
|
||||
for i in [0...nodes.snapshotLength]
|
||||
text.push if data = nodes.snapshotItem(i).data then data else '\n'
|
||||
@info.comment = text.join('').trim().replace /\s+$/gm, ''
|
||||
|
||||
quotes = {}
|
||||
for quotelink in $$ '.quotelink', @nodes.comment
|
||||
# Don't add board links. (>>>/b/)
|
||||
hash = quotelink.hash
|
||||
continue unless hash
|
||||
|
||||
# Don't add catalog links. (>>>/b/catalog or >>>/b/search)
|
||||
pathname = quotelink.pathname
|
||||
continue if /catalog$/.test pathname
|
||||
|
||||
# Don't add rules links. (>>>/a/rules)
|
||||
# Don't add text-board quotelinks. (>>>/img/1234)
|
||||
continue if quotelink.hostname isnt 'boards.4chan.org'
|
||||
|
||||
@nodes.quotelinks.push quotelink
|
||||
|
||||
# Don't count capcode replies as quotes. (Admin/Mod/Dev Replies: ...)
|
||||
continue if quotelink.parentNode.parentNode.className is 'capcodeReplies'
|
||||
|
||||
# Basically, only add quotes that link to posts on an imageboard.
|
||||
quotes["#{pathname.split('/')[1]}.#{hash[2..]}"] = true
|
||||
@quotes = Object.keys quotes
|
||||
@parseComment()
|
||||
@parseQuotes()
|
||||
|
||||
if (file = $ '.file', post) and thumb = $ 'img[data-md5]', file
|
||||
# Supports JPG/PNG/GIF/PDF.
|
||||
@ -145,6 +106,51 @@ class Post
|
||||
g.posts["#{board}.#{@}"] = thread.posts[@] = board.posts[@] = @
|
||||
@kill() if that.isArchived
|
||||
|
||||
parseComment: ->
|
||||
# Get the comment's text.
|
||||
# <br> -> \n
|
||||
# Remove:
|
||||
# 'Comment too long'...
|
||||
# Admin/Mod/Dev replies. (/q/)
|
||||
# EXIF data. (/p/)
|
||||
# Rolls. (/tg/)
|
||||
# Preceding and following new lines.
|
||||
# Trailing spaces.
|
||||
bq = @nodes.comment.cloneNode true
|
||||
for node in $$ '.abbr, .capcodeReplies, .exif, b', bq
|
||||
$.rm node
|
||||
text = []
|
||||
# XPathResult.ORDERED_NODE_SNAPSHOT_TYPE === 7
|
||||
nodes = d.evaluate './/br|.//text()', bq, null, 7, null
|
||||
for i in [0...nodes.snapshotLength]
|
||||
text.push if data = nodes.snapshotItem(i).data then data else '\n'
|
||||
@info.comment = text.join('').trim().replace /\s+$/gm, ''
|
||||
|
||||
parseQuotes: ->
|
||||
quotes = {}
|
||||
for quotelink in $$ '.quotelink', @nodes.comment
|
||||
# Don't add board links. (>>>/b/)
|
||||
hash = quotelink.hash
|
||||
continue unless hash
|
||||
|
||||
# Don't add catalog links. (>>>/b/catalog or >>>/b/search)
|
||||
pathname = quotelink.pathname
|
||||
continue if /catalog$/.test pathname
|
||||
|
||||
# Don't add rules links. (>>>/a/rules)
|
||||
# Don't add text-board quotelinks. (>>>/img/1234)
|
||||
continue if quotelink.hostname isnt 'boards.4chan.org'
|
||||
|
||||
@nodes.quotelinks.push quotelink
|
||||
|
||||
# Don't count capcode replies as quotes. (Admin/Mod/Dev Replies: ...)
|
||||
continue if quotelink.parentNode.parentNode.className is 'capcodeReplies'
|
||||
|
||||
# Basically, only add quotes that link to posts on an imageboard.
|
||||
quotes["#{pathname.split('/')[1]}.#{hash[2..]}"] = true
|
||||
return if @isClone
|
||||
@quotes = Object.keys quotes
|
||||
|
||||
kill: (file, now) ->
|
||||
now or= new Date()
|
||||
if file
|
||||
@ -228,10 +234,7 @@ class Clone extends Post
|
||||
if nodes.date
|
||||
@nodes.date = $ '.dateTime', info
|
||||
|
||||
for quotelink in $$ '.quotelink', @nodes.comment
|
||||
# See comments in Post's constructor.
|
||||
if quotelink.hash or $.hasClass quotelink, 'deadlink'
|
||||
@nodes.quotelinks.push quotelink
|
||||
@parseQuotes()
|
||||
|
||||
if origin.file
|
||||
# Copy values, point to relevant elements.
|
||||
@ -333,6 +336,7 @@ Main =
|
||||
initFeature 'Reveal Spoilers', RevealSpoilers
|
||||
initFeature 'Auto-GIF', AutoGIF
|
||||
initFeature 'Image Hover', ImageHover
|
||||
initFeature 'Comment Expansion', ExpandComment
|
||||
initFeature 'Thread Excerpt', ThreadExcerpt
|
||||
initFeature 'Favicon', Favicon
|
||||
initFeature 'Unread', Unread
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user