Add Thread Hiding.

This commit is contained in:
Nicolas Stepien 2013-01-19 15:40:25 +01:00
parent a7f255fa26
commit 6047151513
4 changed files with 270 additions and 3 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-15
/* 4chan X Alpha - Version 3.0.0 - 2013-01-19
* http://mayhemydg.github.com/4chan-x/
*
* Copyright (c) 2009-2011 James Campos <james.r.campos@gmail.com>
@ -43,7 +43,8 @@
*/
(function() {
var $, $$, Anonymize, AutoGIF, Board, Build, Clone, Conf, Config, FileInfo, Get, ImageHover, Main, Post, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, RevealSpoilers, Sauce, Thread, 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, 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; };
@ -677,6 +678,143 @@
}
});
ThreadHiding = {
init: function() {
if (g.VIEW !== 'index') {
return;
}
this.getHiddenThreads();
this.syncFromCatalog();
this.clean();
return Thread.prototype.callbacks.push({
name: 'Thread Hiding',
cb: this.node
});
},
node: function() {
var _ref;
if (_ref = this.ID, __indexOf.call(ThreadHiding.hiddenThreads.threads, _ref) >= 0) {
ThreadHiding.hide(this);
}
return $.prepend(this.posts[this].nodes.root, ThreadHiding.button(this, '-'));
},
getHiddenThreads: function() {
var hiddenThreads;
hiddenThreads = $.get("hiddenThreads." + g.BOARD);
if (!hiddenThreads) {
hiddenThreads = {
threads: [],
lastChecked: Date.now()
};
$.set("hiddenThreads." + g.BOARD, hiddenThreads);
}
return ThreadHiding.hiddenThreads = hiddenThreads;
},
syncFromCatalog: function() {
var hiddenThreadsOnCatalog;
hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {};
ThreadHiding.hiddenThreads.threads = Object.keys(hiddenThreadsOnCatalog).map(Number);
return $.set("hiddenThreads." + g.BOARD, ThreadHiding.hiddenThreads);
},
clean: function() {
var hiddenThreads, lastChecked, now;
hiddenThreads = ThreadHiding.hiddenThreads;
lastChecked = hiddenThreads.lastChecked;
hiddenThreads.lastChecked = now = Date.now();
if (!(lastChecked < now - $.DAY)) {
return;
}
if (!hiddenThreads.threads.length) {
$.set("hiddenThreads." + g.BOARD, hiddenThreads);
return;
}
return $.ajax("//api.4chan.org/" + g.BOARD + "/catalog.json", {
onload: function() {
var obj, thread, threads, _i, _j, _len, _len1, _ref, _ref1, _ref2;
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 (_ref2 = thread.no, __indexOf.call(hiddenThreads.threads, _ref2) >= 0) {
threads.push(thread.no);
}
}
}
hiddenThreads.threads = threads;
return $.set("hiddenThreads." + g.BOARD, hiddenThreads);
}
});
},
button: function(thread, sign) {
var a;
a = $.el('a', {
className: 'hide-thread-button',
innerHTML: "<span>[&nbsp;" + sign + "&nbsp;]</span>&nbsp;",
href: 'javascript:;'
});
$.on(a, 'click', function() {
return ThreadHiding.toggle(thread);
});
return a;
},
toggle: function(thread) {
var hiddenThreads, hiddenThreadsCatalog;
hiddenThreads = ThreadHiding.getHiddenThreads();
hiddenThreadsCatalog = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {};
if (thread.hidden) {
ThreadHiding.show(thread);
hiddenThreads.threads.splice(hiddenThreads.threads.indexOf(thread.ID), 1);
delete hiddenThreadsCatalog[thread];
} else {
ThreadHiding.hide(thread);
hiddenThreads.threads.push(thread.ID);
hiddenThreadsCatalog[thread] = true;
}
$.set("hiddenThreads." + g.BOARD, hiddenThreads);
return localStorage.setItem("4chan-hide-t-" + g.BOARD, JSON.stringify(hiddenThreadsCatalog));
},
hide: function(thread, makeStub) {
var a, numReplies, op, opInfo, span, threadRoot;
if (makeStub == null) {
makeStub = Conf['Stubs'];
}
if (thread.hidden) {
return;
}
op = thread.posts[thread];
threadRoot = op.nodes.root.parentNode;
threadRoot.hidden = thread.hidden = true;
if (!makeStub) {
threadRoot.nextElementSibling.hidden = true;
return;
}
numReplies = 0;
if (span = $('.summary', threadRoot)) {
numReplies = +span.textContent.match(/\d+/);
}
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, '+');
$.add(a, $.tn("" + opInfo + " (" + numReplies + ")"));
thread.stub = $.el('div');
$.add(thread.stub, a);
return $.before(threadRoot, thread.stub);
},
show: function(thread) {
var threadRoot;
if (thread.stub) {
$.rm(thread.stub);
delete thread.stub;
}
threadRoot = thread.posts[thread].nodes.root.parentNode;
return threadRoot.nextElementSibling.hidden = threadRoot.hidden = thread.hidden = false;
}
};
Redirect = {
image: function(board, filename) {
switch (board) {
@ -2692,6 +2830,13 @@
settings.disableAll = true;
localStorage.setItem('4chan-settings', JSON.stringify(settings));
}
if (Conf['Thread Hiding']) {
try {
ThreadHiding.init();
} catch (err) {
$.log(err, 'Thread Hiding');
}
}
if (Conf['Resurrect Quotes']) {
try {
Quotify.init();
@ -2851,7 +2996,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}"
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}"
};
Main.init();

View File

@ -156,3 +156,8 @@ body.fourchan_x {
max-width: 75%;
padding-bottom: 16px;
}
/* thread hiding */
.opContainer > .hide-thread-button {
float: left;
}

View File

@ -1,3 +1,113 @@
ThreadHiding =
init: ->
return if g.VIEW isnt 'index'
@getHiddenThreads()
@syncFromCatalog()
@clean()
Thread::callbacks.push
name: 'Thread Hiding'
cb: @node
node: ->
if @ID in ThreadHiding.hiddenThreads.threads
ThreadHiding.hide @
$.prepend @posts[@].nodes.root, ThreadHiding.button @, '-'
getHiddenThreads: ->
hiddenThreads = $.get "hiddenThreads.#{g.BOARD}"
unless hiddenThreads
hiddenThreads =
threads: []
lastChecked: Date.now()
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
ThreadHiding.hiddenThreads = hiddenThreads
syncFromCatalog: ->
# Sync hidden threads from the catalog into the index.
hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {}
ThreadHiding.hiddenThreads.threads = Object.keys(hiddenThreadsOnCatalog).map Number
$.set "hiddenThreads.#{g.BOARD}", ThreadHiding.hiddenThreads
clean: ->
{hiddenThreads} = ThreadHiding
{lastChecked} = hiddenThreads
hiddenThreads.lastChecked = now = Date.now()
return unless lastChecked < now - $.DAY
unless hiddenThreads.threads.length
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
return
$.ajax "//api.4chan.org/#{g.BOARD}/catalog.json", onload: ->
threads = []
for obj in JSON.parse @response
for thread in obj.threads
threads.push thread.no if thread.no in hiddenThreads.threads
hiddenThreads.threads = threads
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
button: (thread, sign) ->
a = $.el 'a',
className: 'hide-thread-button'
innerHTML: "<span>[&nbsp;#{sign}&nbsp;]</span>&nbsp;"
href: 'javascript:;'
$.on a, 'click', -> ThreadHiding.toggle thread
a
toggle: (thread) ->
# Get fresh hidden threads.
hiddenThreads = ThreadHiding.getHiddenThreads()
hiddenThreadsCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {}
if thread.hidden
ThreadHiding.show thread
hiddenThreads.threads.splice hiddenThreads.threads.indexOf(thread.ID), 1
delete hiddenThreadsCatalog[thread]
else
ThreadHiding.hide thread
hiddenThreads.threads.push thread.ID
hiddenThreadsCatalog[thread] = true
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
localStorage.setItem "4chan-hide-t-#{g.BOARD}", JSON.stringify hiddenThreadsCatalog
hide: (thread, makeStub=Conf['Stubs']) ->
return if thread.hidden
op = thread.posts[thread]
threadRoot = op.nodes.root.parentNode
threadRoot.hidden = thread.hidden = true
unless makeStub
threadRoot.nextElementSibling.hidden = true # <hr>
return
numReplies = 0
if span = $ '.summary', threadRoot
numReplies = +span.textContent.match /\d+/
numReplies += $$('.opContainer ~ .replyContainer', threadRoot).length
numReplies = if numReplies is 1 then '1 reply' else "#{numReplies} replies"
opInfo =
if Conf['Anonymize']
'Anonymous'
else
$('.nameBlock', op.nodes.info).textContent
a = ThreadHiding.button 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]
$.before threadRoot, thread.stub
show: (thread) ->
if thread.stub
$.rm thread.stub
delete thread.stub
threadRoot = thread.posts[thread].nodes.root.parentNode
threadRoot.nextElementSibling.hidden =
threadRoot.hidden = thread.hidden = false
Redirect =
image: (board, filename) ->
# XXX need to differentiate between thumbnail only and full_image for img src=

View File

@ -339,6 +339,13 @@ Main =
settings.disableAll = true
localStorage.setItem '4chan-settings', JSON.stringify settings
if Conf['Thread Hiding']
try
ThreadHiding.init()
catch err
# XXX handle error
$.log err, 'Thread Hiding'
if Conf['Resurrect Quotes']
try
Quotify.init()