Add Reply Hiding.

Make Thread & Reply Hiding work with other features.
Tiny fixes.
This commit is contained in:
Nicolas Stepien 2013-01-22 13:28:25 +01:00
parent 6047151513
commit 8a587f515c
4 changed files with 352 additions and 94 deletions

View File

@ -20,7 +20,7 @@
// @icon https://github.com/MayhemYDG/4chan-x/raw/stable/img/icon.gif
// ==/UserScript==
/* 4chan X Alpha - Version 3.0.0 - 2013-01-19
/* 4chan X Alpha - Version 3.0.0 - 2013-01-22
* http://mayhemydg.github.com/4chan-x/
*
* Copyright (c) 2009-2011 James Campos <james.r.campos@gmail.com>
@ -43,7 +43,7 @@
*/
(function() {
var $, $$, Anonymize, AutoGIF, Board, Build, Clone, Conf, Config, FileInfo, Get, ImageHover, Main, Post, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, RevealSpoilers, Sauce, Thread, ThreadHiding, ThreadUpdater, Time, UI, d, g, _base,
var $, $$, Anonymize, AutoGIF, Board, Build, Clone, Conf, Config, FileInfo, Get, ImageHover, Main, Post, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, RevealSpoilers, Sauce, Thread, ThreadHiding, ThreadUpdater, Time, UI, d, g, _base,
__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; };
@ -696,7 +696,7 @@
if (_ref = this.ID, __indexOf.call(ThreadHiding.hiddenThreads.threads, _ref) >= 0) {
ThreadHiding.hide(this);
}
return $.prepend(this.posts[this].nodes.root, ThreadHiding.button(this, '-'));
return $.prepend(this.posts[this].nodes.root, ThreadHiding.makeButton(this, '-'));
},
getHiddenThreads: function() {
var hiddenThreads;
@ -721,7 +721,7 @@
hiddenThreads = ThreadHiding.hiddenThreads;
lastChecked = hiddenThreads.lastChecked;
hiddenThreads.lastChecked = now = Date.now();
if (!(lastChecked < now - $.DAY)) {
if (lastChecked > now - $.DAY) {
return;
}
if (!hiddenThreads.threads.length) {
@ -748,7 +748,7 @@
}
});
},
button: function(thread, sign) {
makeButton: function(thread, sign) {
var a;
a = $.el('a', {
className: 'hide-thread-button',
@ -764,7 +764,7 @@
var hiddenThreads, hiddenThreadsCatalog;
hiddenThreads = ThreadHiding.getHiddenThreads();
hiddenThreadsCatalog = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {};
if (thread.hidden) {
if (thread.isHidden) {
ThreadHiding.show(thread);
hiddenThreads.threads.splice(hiddenThreads.threads.indexOf(thread.ID), 1);
delete hiddenThreadsCatalog[thread];
@ -786,7 +786,7 @@
}
op = thread.posts[thread];
threadRoot = op.nodes.root.parentNode;
threadRoot.hidden = thread.hidden = true;
threadRoot.hidden = thread.isHidden = true;
if (!makeStub) {
threadRoot.nextElementSibling.hidden = true;
return;
@ -798,7 +798,7 @@
numReplies += $$('.opContainer ~ .replyContainer', threadRoot).length;
numReplies = numReplies === 1 ? '1 reply' : "" + numReplies + " replies";
opInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', op.nodes.info).textContent;
a = ThreadHiding.button(thread, '+');
a = ThreadHiding.makeButton(thread, '+');
$.add(a, $.tn("" + opInfo + " (" + numReplies + ")"));
thread.stub = $.el('div');
$.add(thread.stub, a);
@ -811,7 +811,141 @@
delete thread.stub;
}
threadRoot = thread.posts[thread].nodes.root.parentNode;
return threadRoot.nextElementSibling.hidden = threadRoot.hidden = thread.hidden = false;
return threadRoot.nextElementSibling.hidden = threadRoot.hidden = thread.isHidden = false;
}
};
ReplyHiding = {
init: function() {
this.getHiddenPosts();
this.clean();
return Post.prototype.callbacks.push({
name: 'Reply Hiding',
cb: this.node
});
},
node: function() {
var thread, _ref;
if (!this.isReply || this.isClone) {
return;
}
if (thread = ReplyHiding.hiddenPosts.threads[this.thread]) {
if (_ref = this.ID, __indexOf.call(thread, _ref) >= 0) {
ReplyHiding.hide(this);
}
}
return $.replace($('.sideArrows', this.nodes.root), ReplyHiding.makeButton(this, '-'));
},
getHiddenPosts: function() {
var hiddenPosts;
hiddenPosts = $.get("hiddenPosts." + g.BOARD);
if (!hiddenPosts) {
hiddenPosts = {
threads: {},
lastChecked: Date.now()
};
$.set("hiddenPosts." + g.BOARD, hiddenPosts);
}
return ReplyHiding.hiddenPosts = hiddenPosts;
},
clean: function() {
var hiddenPosts, lastChecked, now;
hiddenPosts = ReplyHiding.hiddenPosts;
lastChecked = hiddenPosts.lastChecked;
hiddenPosts.lastChecked = now = Date.now();
if (lastChecked > now - $.DAY) {
return;
}
if (!Object.keys(hiddenPosts.threads).length) {
$.set("hiddenPosts." + g.BOARD, hiddenPosts);
return;
}
return $.ajax("//api.4chan.org/" + g.BOARD + "/catalog.json", {
onload: function() {
var obj, thread, threads, _i, _j, _len, _len1, _ref, _ref1;
threads = {};
_ref = JSON.parse(this.response);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
obj = _ref[_i];
_ref1 = obj.threads;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
thread = _ref1[_j];
if (thread.no in hiddenPosts.threads) {
threads[thread.no] = hiddenPosts.threads[thread.no];
}
}
}
hiddenPosts.threads = threads;
return $.set("hiddenPosts." + g.BOARD, hiddenPosts);
}
});
},
makeButton: function(post, sign) {
var a;
a = $.el('a', {
className: 'hide-reply-button',
innerHTML: "<span>[&nbsp;" + sign + "&nbsp;]</span>",
href: 'javascript:;'
});
$.on(a, 'click', function() {
return ReplyHiding.toggle(post);
});
return a;
},
toggle: function(post) {
var hiddenPosts, quotelink, quotelinks, thread, _i, _j, _len, _len1;
hiddenPosts = ReplyHiding.getHiddenPosts();
quotelinks = Get.allQuotelinksLinkingTo(post);
if (post.isHidden) {
ReplyHiding.show(post);
for (_i = 0, _len = quotelinks.length; _i < _len; _i++) {
quotelink = quotelinks[_i];
$.rmClass(quotelink, 'filtered');
}
thread = hiddenPosts.threads[post.thread];
if (thread.length === 1) {
delete hiddenPosts.threads[post.thread];
} else {
thread.splice(thread.indexOf(post.ID), 1);
}
} else {
ReplyHiding.hide(post);
for (_j = 0, _len1 = quotelinks.length; _j < _len1; _j++) {
quotelink = quotelinks[_j];
$.addClass(quotelink, 'filtered');
}
if (!(thread = hiddenPosts.threads[post.thread])) {
thread = hiddenPosts.threads[post.thread] = [];
}
thread.push(post.ID);
}
return $.set("hiddenPosts." + g.BOARD, hiddenPosts);
},
hide: function(post, makeStub) {
var a, postInfo;
if (makeStub == null) {
makeStub = Conf['Stubs'];
}
if (post.isHidden) {
return;
}
post.nodes.root.hidden = post.isHidden = true;
if (!makeStub) {
return;
}
a = ReplyHiding.makeButton(post, '+');
postInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', post.nodes.info).textContent;
$.add(a, $.tn(" " + postInfo));
post.stub = $.el('div');
$.add(post.stub, a);
return $.before(post.nodes.root, post.stub);
},
show: function(post) {
if (post.stub) {
$.rm(post.stub);
delete post.stub;
}
return post.nodes.root.hidden = post.isHidden = false;
}
};
@ -1132,7 +1266,7 @@
},
postDataFromLink: function(link) {
var board, path, postID, threadID;
if (link.host === 'boards.4chan.org') {
if (link.hostname === 'boards.4chan.org') {
path = link.pathname.split('/');
board = path[1];
threadID = path[3];
@ -1148,6 +1282,39 @@
postID: +postID
};
},
allQuotelinksLinkingTo: function(post) {
var ID, fullID, quote, quotedPost, quotelinks, quoterPost, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3;
quotelinks = [];
fullID = "" + post.board + "." + post;
_ref = g.posts;
for (ID in _ref) {
quoterPost = _ref[ID];
if (-1 !== quoterPost.quotes.indexOf(fullID)) {
_ref1 = [quoterPost].concat(quoterPost.clones);
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
quoterPost = _ref1[_i];
quotelinks.push.apply(quotelinks, quoterPost.nodes.quotelinks);
}
}
}
if (Conf['Quote Backlinks']) {
_ref2 = post.quotes;
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
quote = _ref2[_j];
quotedPost = g.posts[quote];
_ref3 = [quotedPost].concat(quotedPost.clones);
for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) {
quotedPost = _ref3[_k];
quotelinks.push.apply(quotelinks, Array.prototype.slice.call(quotedPost.nodes.backlinks));
}
}
}
return quotelinks.filter(function(quotelink) {
var board, postID, _ref4;
_ref4 = Get.postDataFromLink(quotelink), board = _ref4.board, postID = _ref4.postID;
return board === post.board.ID && postID === post.ID;
});
},
contextFromLink: function(quotelink) {
return Get.postFromRoot($.x('ancestor::div[parent::div[@class="thread"]][1]', quotelink));
},
@ -1595,7 +1762,7 @@
}
a = $.el('a', {
href: "/" + this.board + "/res/" + this.thread + "#p" + this,
className: 'backlink',
className: this.isHidden ? 'filtered backlink' : 'backlink',
textContent: QuoteBacklink.funk(this.ID)
});
_ref = this.quotes;
@ -2027,7 +2194,7 @@
},
node: function() {
var URL, gif, style, thumb, _ref, _ref1;
if (this.isClone || !((_ref = this.file) != null ? _ref.isImage : void 0)) {
if (this.isClone || this.isHidden || this.thread.isHidden || !((_ref = this.file) != null ? _ref.isImage : void 0)) {
return;
}
_ref1 = this.file, thumb = _ref1.thumb, URL = _ref1.URL;
@ -2576,7 +2743,7 @@
}
Post.prototype.kill = function(img) {
var ID, board, num, post, postID, quote, quotelink, quotelinks, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3, _ref4;
var quotelink, _i, _len, _ref;
if (this.file && !this.file.isDead) {
this.file.isDead = true;
}
@ -2585,41 +2752,11 @@
}
this.isDead = true;
$.addClass(this.nodes.root, 'dead');
quotelinks = [];
num = "" + this.board + "." + this;
_ref = g.posts;
for (ID in _ref) {
post = _ref[ID];
if (-1 < post.quotes.indexOf(num)) {
_ref1 = [post].concat(post.clones);
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
post = _ref1[_i];
quotelinks.push.apply(quotelinks, post.nodes.quotelinks);
}
}
}
if (Conf['Quote Backlinks']) {
_ref2 = this.quotes;
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
quote = _ref2[_j];
post = g.posts[quote];
_ref3 = [post].concat(post.clones);
for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) {
post = _ref3[_k];
quotelinks.push.apply(quotelinks, Array.prototype.slice.call(post.nodes.backlinks));
}
}
}
for (_l = 0, _len3 = quotelinks.length; _l < _len3; _l++) {
quotelink = quotelinks[_l];
if ($.hasClass(quotelink, 'deadlink')) {
continue;
}
_ref4 = Get.postDataFromLink(quotelink), board = _ref4.board, postID = _ref4.postID;
if (board === this.board.ID(postID === this.ID)) {
$.add(quotelink, $.tn('\u00A0(Dead)'));
$.addClass(quotelinks, 'deadlink');
}
_ref = Get.allQuotelinksLinkingTo(this);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
quotelink = _ref[_i];
$.add(quotelink, $.tn('\u00A0(Dead)'));
$.addClass(quotelink, 'deadlink');
}
};
@ -2676,6 +2813,7 @@
inlined = _ref2[_k];
$.rmClass(inlined, 'inlined');
}
root.hidden = false;
$.rmClass(root, 'forwarded');
if (nodes.subject) {
this.nodes.subject = $('.subject', info);
@ -2837,6 +2975,13 @@
$.log(err, 'Thread Hiding');
}
}
if (Conf['Reply Hiding']) {
try {
ReplyHiding.init();
} catch (err) {
$.log(err, 'Reply Hiding');
}
}
if (Conf['Resurrect Quotes']) {
try {
Quotify.init();
@ -2996,7 +3141,7 @@
settings: function() {
return alert('Here be settings');
},
css: "/* general */\n.dialog.reply {\n display: block;\n border: 1px solid rgba(0, 0, 0, .25);\n padding: 0;\n}\n.move {\n cursor: move;\n}\nlabel {\n cursor: pointer;\n}\na[href=\"javascript:;\"] {\n text-decoration: none;\n}\n.warning {\n color: red;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\n display: block !important;\n}\n.post {\n overflow: visible !important;\n}\n\n/* fixed, z-index */\n#qp, #ihover,\n#updater, #stats,\n#boardNavDesktop.reply,\n#qr, #watcher {\n position: fixed;\n}\n#qp, #ihover {\n z-index: 100;\n}\n#updater, #stats {\n z-index: 90;\n}\n#boardNavDesktop.reply:hover {\n z-index: 80;\n}\n#qr {\n z-index: 50;\n}\n#watcher {\n z-index: 30;\n}\n#boardNavDesktop.reply {\n z-index: 10;\n}\n\n\n/* header */\nbody.fourchan_x {\n margin-top: 2.5em;\n}\n#boardNavDesktop.reply {\n border-width: 0 0 1px;\n padding: 4px;\n top: 0;\n right: 0;\n left: 0;\n transition: opacity .1s ease-in-out;\n -o-transition: opacity .1s ease-in-out;\n -moz-transition: opacity .1s ease-in-out;\n -webkit-transition: opacity .1s ease-in-out;\n}\n#boardNavDesktop.reply:not(:hover) {\n opacity: .4;\n transition: opacity 1.5s .5s ease-in-out;\n -o-transition: opacity 1.5s .5s ease-in-out;\n -moz-transition: opacity 1.5s .5s ease-in-out;\n -webkit-transition: opacity 1.5s .5s ease-in-out;\n}\n#boardNavDesktop.reply a {\n margin: -1px;\n}\n#settings {\n float: right;\n}\n\n/* thread updater */\n#updater {\n text-align: right;\n}\n#updater:not(:hover) {\n background: none;\n border: none;\n}\n#updater input[type=number] {\n width: 4em;\n}\n#updater:not(:hover) > div:not(.move) {\n display: none;\n}\n.new {\n color: limegreen;\n}\n\n/* quote */\n.quotelink.deadlink {\n text-decoration: underline !important;\n}\n.deadlink:not(.quotelink) {\n text-decoration: none !important;\n}\n.inlined {\n opacity: .5;\n}\n#qp input, .forwarded {\n display: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\n text-decoration: none;\n border-bottom: 1px dashed;\n}\n.inline {\n border: 1px solid rgba(128, 128, 128, .5);\n display: table;\n margin: 2px 0;\n}\n.inline .post {\n border: 0 !important;\n display: table !important;\n margin: 0 !important;\n padding: 1px 2px !important;\n}\n#qp {\n padding: 2px 2px 5px;\n}\n#qp .post {\n border: none;\n margin: 0;\n padding: 0;\n}\n#qp img {\n max-height: 300px;\n max-width: 500px;\n}\n.qphl {\n box-shadow: 0 0 0 2px rgba(216, 94, 49, .7);\n}\n\n/* file */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull {\n display: none;\n}\n#ihover {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n max-height: 100%;\n max-width: 75%;\n padding-bottom: 16px;\n}\n\n/* thread hiding */\n.opContainer > .hide-thread-button {\n float: left;\n}"
css: "/* general */\n.dialog.reply {\n display: block;\n border: 1px solid rgba(0, 0, 0, .25);\n padding: 0;\n}\n.move {\n cursor: move;\n}\nlabel {\n cursor: pointer;\n}\na[href=\"javascript:;\"] {\n text-decoration: none;\n}\n.warning {\n color: red;\n}\n\n/* 4chan style fixes */\n.opContainer, .op {\n display: block !important;\n}\n.post {\n overflow: visible !important;\n}\n\n/* fixed, z-index */\n#qp, #ihover,\n#updater, #stats,\n#boardNavDesktop.reply,\n#qr, #watcher {\n position: fixed;\n}\n#qp, #ihover {\n z-index: 100;\n}\n#updater, #stats {\n z-index: 90;\n}\n#boardNavDesktop.reply:hover {\n z-index: 80;\n}\n#qr {\n z-index: 50;\n}\n#watcher {\n z-index: 30;\n}\n#boardNavDesktop.reply {\n z-index: 10;\n}\n\n\n/* header */\nbody.fourchan_x {\n margin-top: 2.5em;\n}\n#boardNavDesktop.reply {\n border-width: 0 0 1px;\n padding: 4px;\n top: 0;\n right: 0;\n left: 0;\n transition: opacity .1s ease-in-out;\n -o-transition: opacity .1s ease-in-out;\n -moz-transition: opacity .1s ease-in-out;\n -webkit-transition: opacity .1s ease-in-out;\n}\n#boardNavDesktop.reply:not(:hover) {\n opacity: .4;\n transition: opacity 1.5s .5s ease-in-out;\n -o-transition: opacity 1.5s .5s ease-in-out;\n -moz-transition: opacity 1.5s .5s ease-in-out;\n -webkit-transition: opacity 1.5s .5s ease-in-out;\n}\n#boardNavDesktop.reply a {\n margin: -1px;\n}\n#settings {\n float: right;\n}\n\n/* thread updater */\n#updater {\n text-align: right;\n}\n#updater:not(:hover) {\n background: none;\n border: none;\n}\n#updater input[type=number] {\n width: 4em;\n}\n#updater:not(:hover) > div:not(.move) {\n display: none;\n}\n.new {\n color: limegreen;\n}\n\n/* quote */\n.quotelink.deadlink {\n text-decoration: underline !important;\n}\n.deadlink:not(.quotelink) {\n text-decoration: none !important;\n}\n.inlined {\n opacity: .5;\n}\n#qp input, .forwarded {\n display: none;\n}\n.quotelink.forwardlink,\n.backlink.forwardlink {\n text-decoration: none;\n border-bottom: 1px dashed;\n}\n.filtered {\n text-decoration: underline line-through;\n}\n.inline {\n border: 1px solid rgba(128, 128, 128, .5);\n display: table;\n margin: 2px 0;\n}\n.inline .post {\n border: 0 !important;\n display: table !important;\n margin: 0 !important;\n padding: 1px 2px !important;\n}\n#qp {\n padding: 2px 2px 5px;\n}\n#qp .post {\n border: none;\n margin: 0;\n padding: 0;\n}\n#qp img {\n max-height: 300px;\n max-width: 500px;\n}\n.qphl {\n box-shadow: 0 0 0 2px rgba(216, 94, 49, .7);\n}\n\n/* file */\n.fileText:hover .fntrunc,\n.fileText:not(:hover) .fnfull {\n display: none;\n}\n#ihover {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n max-height: 100%;\n max-width: 75%;\n padding-bottom: 16px;\n}\n\n/* thread hiding */\n.opContainer > .hide-thread-button {\n float: left;\n}\n\n/* reply hiding */\n.replyContainer > .hide-reply-button {\n float: left;\n margin-right: 2px;\n}"
};
Main.init();

View File

@ -117,6 +117,9 @@ body.fourchan_x {
text-decoration: none;
border-bottom: 1px dashed;
}
.filtered {
text-decoration: underline line-through;
}
.inline {
border: 1px solid rgba(128, 128, 128, .5);
display: table;
@ -161,3 +164,9 @@ body.fourchan_x {
.opContainer > .hide-thread-button {
float: left;
}
/* reply hiding */
.replyContainer > .hide-reply-button {
float: left;
margin-right: 2px;
}

View File

@ -11,7 +11,7 @@ ThreadHiding =
node: ->
if @ID in ThreadHiding.hiddenThreads.threads
ThreadHiding.hide @
$.prepend @posts[@].nodes.root, ThreadHiding.button @, '-'
$.prepend @posts[@].nodes.root, ThreadHiding.makeButton @, '-'
getHiddenThreads: ->
hiddenThreads = $.get "hiddenThreads.#{g.BOARD}"
@ -33,7 +33,7 @@ ThreadHiding =
{lastChecked} = hiddenThreads
hiddenThreads.lastChecked = now = Date.now()
return unless lastChecked < now - $.DAY
return if lastChecked > now - $.DAY
unless hiddenThreads.threads.length
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
@ -47,7 +47,7 @@ ThreadHiding =
hiddenThreads.threads = threads
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
button: (thread, sign) ->
makeButton: (thread, sign) ->
a = $.el 'a',
className: 'hide-thread-button'
innerHTML: "<span>[&nbsp;#{sign}&nbsp;]</span>&nbsp;"
@ -59,7 +59,7 @@ ThreadHiding =
# Get fresh hidden threads.
hiddenThreads = ThreadHiding.getHiddenThreads()
hiddenThreadsCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {}
if thread.hidden
if thread.isHidden
ThreadHiding.show thread
hiddenThreads.threads.splice hiddenThreads.threads.indexOf(thread.ID), 1
delete hiddenThreadsCatalog[thread]
@ -74,7 +74,7 @@ ThreadHiding =
return if thread.hidden
op = thread.posts[thread]
threadRoot = op.nodes.root.parentNode
threadRoot.hidden = thread.hidden = true
threadRoot.hidden = thread.isHidden = true
unless makeStub
threadRoot.nextElementSibling.hidden = true # <hr>
@ -91,13 +91,12 @@ ThreadHiding =
else
$('.nameBlock', op.nodes.info).textContent
a = ThreadHiding.button thread, '+'
a = ThreadHiding.makeButton thread, '+'
$.add a, $.tn "#{opInfo} (#{numReplies})"
thread.stub = $.el 'div'
$.add thread.stub, a
# if Conf['Menu']
# menuButton = Menu.button()
# $.add thread.stub, [$.tn(' '), menuButton]
# $.add thread.stub, [$.tn(' '), Menu.makeButton()]
$.before threadRoot, thread.stub
show: (thread) ->
@ -106,7 +105,106 @@ ThreadHiding =
delete thread.stub
threadRoot = thread.posts[thread].nodes.root.parentNode
threadRoot.nextElementSibling.hidden =
threadRoot.hidden = thread.hidden = false
threadRoot.hidden = thread.isHidden = false
ReplyHiding =
init: ->
@getHiddenPosts()
@clean()
Post::callbacks.push
name: 'Reply Hiding'
cb: @node
node: ->
return if !@isReply or @isClone
if thread = ReplyHiding.hiddenPosts.threads[@thread]
if @ID in thread
ReplyHiding.hide @
$.replace $('.sideArrows', @nodes.root), ReplyHiding.makeButton @, '-'
getHiddenPosts: ->
hiddenPosts = $.get "hiddenPosts.#{g.BOARD}"
unless hiddenPosts
hiddenPosts =
threads: {}
lastChecked: Date.now()
$.set "hiddenPosts.#{g.BOARD}", hiddenPosts
ReplyHiding.hiddenPosts = hiddenPosts
clean: ->
{hiddenPosts} = ReplyHiding
{lastChecked} = hiddenPosts
hiddenPosts.lastChecked = now = Date.now()
return if lastChecked > now - $.DAY
unless Object.keys(hiddenPosts.threads).length
$.set "hiddenPosts.#{g.BOARD}", hiddenPosts
return
$.ajax "//api.4chan.org/#{g.BOARD}/catalog.json", onload: ->
threads = {}
for obj in JSON.parse @response
for thread in obj.threads
if thread.no of hiddenPosts.threads
threads[thread.no] = hiddenPosts.threads[thread.no]
hiddenPosts.threads = threads
$.set "hiddenPosts.#{g.BOARD}", hiddenPosts
makeButton: (post, sign) ->
a = $.el 'a',
className: 'hide-reply-button'
innerHTML: "<span>[&nbsp;#{sign}&nbsp;]</span>"
href: 'javascript:;'
$.on a, 'click', -> ReplyHiding.toggle post
a
toggle: (post) ->
# Get fresh hidden posts.
hiddenPosts = ReplyHiding.getHiddenPosts()
quotelinks = Get.allQuotelinksLinkingTo post
if post.isHidden
ReplyHiding.show post
for quotelink in quotelinks
$.rmClass quotelink, 'filtered'
# XXX recursive filtering
thread = hiddenPosts.threads[post.thread]
if thread.length is 1
delete hiddenPosts.threads[post.thread]
else
thread.splice thread.indexOf(post.ID), 1
else
ReplyHiding.hide post
for quotelink in quotelinks
$.addClass quotelink, 'filtered'
unless thread = hiddenPosts.threads[post.thread]
thread = hiddenPosts.threads[post.thread] = []
thread.push post.ID
$.set "hiddenPosts.#{g.BOARD}", hiddenPosts
hide: (post, makeStub=Conf['Stubs']) ->
return if post.isHidden
post.nodes.root.hidden = post.isHidden = true
return unless makeStub
a = ReplyHiding.makeButton post, '+'
postInfo =
if Conf['Anonymize']
'Anonymous'
else
$('.nameBlock', post.nodes.info).textContent
$.add a, $.tn " #{postInfo}"
post.stub = $.el 'div'
$.add post.stub, a
# if Conf['Menu']
# $.add post.stub, [$.tn(' '), Menu.makeButton()]
$.before post.nodes.root, post.stub
show: (post) ->
if post.stub
$.rm post.stub
delete post.stub
post.nodes.root.hidden = post.isHidden = false
Redirect =
image: (board, filename) ->
@ -464,7 +562,7 @@ Get =
post = g.posts["#{board}.#{postID}"]
if index then post.clones[index] else post
postDataFromLink: (link) ->
if link.host is 'boards.4chan.org'
if link.hostname is 'boards.4chan.org'
path = link.pathname.split '/'
board = path[1]
threadID = path[3]
@ -478,6 +576,33 @@ Get =
threadID: +threadID
postID: +postID
}
allQuotelinksLinkingTo: (post) ->
# Get quotelinks & backlinks linking to the given post.
quotelinks = []
fullID = "#{post.board}.#{post}"
# First:
# In every posts,
# if it did quote this post,
# get all their backlinks.
for ID, quoterPost of g.posts
if -1 isnt quoterPost.quotes.indexOf fullID
for quoterPost in [quoterPost].concat quoterPost.clones
quotelinks.push.apply quotelinks, quoterPost.nodes.quotelinks
# Second:
# If we have quote backlinks:
# in all posts this post quoted
# and their clones,
# get all of their backlinks.
if Conf['Quote Backlinks']
for quote in post.quotes
quotedPost = g.posts[quote]
for quotedPost in [quotedPost].concat quotedPost.clones
quotelinks.push.apply quotelinks, Array::slice.call quotedPost.nodes.backlinks
# Third:
# Filter out irrelevant quotelinks.
quotelinks.filter (quotelink) ->
{board, postID} = Get.postDataFromLink quotelink
board is post.board.ID and postID is post.ID
contextFromLink: (quotelink) ->
Get.postFromRoot $.x 'ancestor::div[parent::div[@class="thread"]][1]', quotelink
postClone: (board, threadID, postID, root, context) ->
@ -886,8 +1011,7 @@ QuoteBacklink =
return if @isClone or !@quotes.length
a = $.el 'a',
href: "/#{@board}/res/#{@thread}#p#{@}"
# XXX className: if post.el.hidden then 'filtered backlink' else 'backlink'
className: 'backlink'
className: if @isHidden then 'filtered backlink' else 'backlink'
textContent: QuoteBacklink.funk @ID
for quote in @quotes
containers = [QuoteBacklink.getContainer quote]
@ -1155,8 +1279,7 @@ AutoGIF =
name: 'Auto-GIF'
cb: @node
node: ->
# XXX return if @hidden?
return if @isClone or !@file?.isImage
return if @isClone or @isHidden or @thread.isHidden or !@file?.isImage
{thumb, URL} = @file
return unless /gif$/.test(URL) and !/spoiler/.test thumb.src
if @file.isSpoiler

View File

@ -153,37 +153,11 @@ class Post
$.addClass @nodes.root, 'dead'
# XXX style dead posts.
# Get quote/backlinks to this post,
# Get quote/backlinks to this post
# and paint them (Dead).
# First:
# In every posts,
# if it did quote this post,
# get all their backlinks.
# Second:
# If we have quote backlinks,
# in all posts this post quoted,
# and their clones,
# get all of their backlinks.
# Third:
# In all collected links,
# apply (Dead) if relevant.
quotelinks = []
num = "#{@board}.#{@}"
for ID, post of g.posts
if -1 < post.quotes.indexOf num
for post in [post].concat post.clones
quotelinks.push.apply quotelinks, post.nodes.quotelinks
if Conf['Quote Backlinks']
for quote in @quotes
post = g.posts[quote]
for post in [post].concat post.clones
quotelinks.push.apply quotelinks, Array::slice.call post.nodes.backlinks
for quotelink in quotelinks
continue if $.hasClass quotelink, 'deadlink'
{board, postID} = Get.postDataFromLink quotelink
if board is @board.ID postID is @ID
$.add quotelink, $.tn '\u00A0(Dead)'
$.addClass quotelinks, 'deadlink'
for quotelink in Get.allQuotelinksLinkingTo @
$.add quotelink, $.tn '\u00A0(Dead)'
$.addClass quotelink, 'deadlink'
return
addClone: (context) ->
new Clone @, context
@ -217,7 +191,7 @@ class Clone extends Post
for inlined in $$ '.inlined', post
$.rmClass inlined, 'inlined'
# root.hidden = false # post hiding
root.hidden = false # post hiding
$.rmClass root, 'forwarded' # quote inlining
# $.rmClass post, 'highlight' # keybind navigation
@ -346,6 +320,13 @@ Main =
# XXX handle error
$.log err, 'Thread Hiding'
if Conf['Reply Hiding']
try
ReplyHiding.init()
catch err
# XXX handle error
$.log err, 'Reply Hiding'
if Conf['Resurrect Quotes']
try
Quotify.init()