Add Thread Hiding in the Menu, with stub setting.

Add an option to toggle Thread/Reply Hiding Buttons.
Save hiding settings of individual threads, like if we want to see its stub or not when we reload the
page.
Add the menu buttons in stubs.
Make the open function for menu entries optional.
This commit is contained in:
Nicolas Stepien 2013-01-26 04:24:03 +01:00
parent 7bf2e7dd45
commit d8b0b74df0
5 changed files with 210 additions and 57 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-25
/* 4chan X Alpha - Version 3.0.0 - 2013-01-26
* http://mayhemydg.github.com/4chan-x/
*
* Copyright (c) 2009-2011 James Campos <james.r.campos@gmail.com>
@ -66,8 +66,9 @@
'Anonymize': [false, 'Turn everyone Anonymous.'],
'Filter': [true, 'Self-moderation placebo.'],
'Recursive Hiding': [true, 'Filter replies of filtered posts, recursively.'],
'Reply Hiding': [true, 'Hide single replies.'],
'Thread Hiding': [true, 'Hide entire threads.'],
'Reply Hiding': [true, 'Hide single replies.'],
'Thread/Reply Hiding Buttons': [true, 'Make buttons to hide threads / replies, in addition to menu links.'],
'Stubs': [true, 'Make stubs of hidden threads / replies.']
},
Imaging: {
@ -692,9 +693,12 @@
});
},
node: function() {
var _ref;
if (_ref = this.ID, __indexOf.call(ThreadHiding.hiddenThreads.threads, _ref) >= 0) {
ThreadHiding.hide(this);
var data;
if (data = ThreadHiding.hiddenThreads.threads[this]) {
ThreadHiding.hide(this, data.makeStub);
}
if (!Conf['Thread/Reply Hiding Buttons']) {
return;
}
return $.prepend(this.posts[this].nodes.root, ThreadHiding.makeButton(this, 'hide'));
},
@ -703,7 +707,7 @@
hiddenThreads = $.get("hiddenThreads." + g.BOARD);
if (!hiddenThreads) {
hiddenThreads = {
threads: [],
threads: {},
lastChecked: Date.now()
};
$.set("hiddenThreads." + g.BOARD, hiddenThreads);
@ -711,9 +715,21 @@
return ThreadHiding.hiddenThreads = hiddenThreads;
},
syncFromCatalog: function() {
var hiddenThreadsOnCatalog;
var hiddenThreadsOnCatalog, threadID, threads;
hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {};
ThreadHiding.hiddenThreads.threads = Object.keys(hiddenThreadsOnCatalog).map(Number);
threads = ThreadHiding.hiddenThreads.threads;
for (threadID in hiddenThreadsOnCatalog) {
if (threadID in threads) {
continue;
}
threads[threadID] = {};
}
for (threadID in threads) {
if (threadID in threads) {
continue;
}
delete threads[threadID];
}
return $.set("hiddenThreads." + g.BOARD, ThreadHiding.hiddenThreads);
},
clean: function() {
@ -724,22 +740,22 @@
if (lastChecked > now - $.DAY) {
return;
}
if (!hiddenThreads.threads.length) {
if (!Object.keys(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 = [];
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 (_ref2 = thread.no, __indexOf.call(hiddenThreads.threads, _ref2) >= 0) {
threads.push(thread.no);
if (thread.no in hiddenThreads.threads) {
threads[thread.no] = hiddenThreads.threads[thread.no];
}
}
}
@ -748,6 +764,53 @@
}
});
},
menu: {
init: function() {
var apply, div, option;
if (g.VIEW !== 'index') {
return;
}
div = $.el('div', {
className: 'hide-thread-link',
textContent: 'Hide thread'
});
option = $.el('label', {
innerHTML: "<input type=checkbox checked=" + Conf['Stubs'] + "> Make stub"
});
apply = $.el('a', {
textContent: 'Apply',
href: 'javascript:;'
});
$.on(apply, 'click', ThreadHiding.menu.hide);
return Menu.addEntry({
el: div,
open: function(post) {
var thread;
thread = post.thread;
if (post.isReply || thread.isHidden) {
return false;
}
ThreadHiding.menu.thread = thread;
return true;
},
children: [
{
el: option
}, {
el: apply
}
]
});
},
hide: function() {
var makeStub, thread;
makeStub = $('input', this.parentNode).checked;
thread = ThreadHiding.menu.thread;
ThreadHiding.hide(thread, makeStub);
ThreadHiding.saveHiddenState(thread, makeStub);
return Menu.close();
}
},
makeButton: function(thread, type) {
var a;
a = $.el('a', {
@ -760,22 +823,30 @@
});
return a;
},
toggle: function(thread) {
saveHiddenState: function(thread, makeStub) {
var hiddenThreads, hiddenThreadsCatalog;
hiddenThreads = ThreadHiding.getHiddenThreads();
hiddenThreadsCatalog = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {};
if (thread.isHidden) {
ThreadHiding.show(thread);
hiddenThreads.threads.splice(hiddenThreads.threads.indexOf(thread.ID), 1);
delete hiddenThreadsCatalog[thread];
} else {
ThreadHiding.hide(thread);
hiddenThreads.threads.push(thread.ID);
hiddenThreads.threads[thread] = {
makeStub: makeStub
};
hiddenThreadsCatalog[thread] = true;
} else {
delete hiddenThreads.threads[thread];
delete hiddenThreadsCatalog[thread];
}
$.set("hiddenThreads." + g.BOARD, hiddenThreads);
return localStorage.setItem("4chan-hide-t-" + g.BOARD, JSON.stringify(hiddenThreadsCatalog));
},
toggle: function(thread) {
if (thread.isHidden) {
ThreadHiding.show(thread);
} else {
ThreadHiding.hide(thread);
}
return ThreadHiding.saveHiddenState(thread);
},
hide: function(thread, makeStub) {
var a, numReplies, op, opInfo, span, threadRoot;
if (makeStub == null) {
@ -804,6 +875,9 @@
className: 'stub'
});
$.add(thread.stub, a);
if (Conf['Menu']) {
$.add(thread.stub, [$.tn(' '), Menu.makeButton(op)]);
}
return $.before(threadRoot, thread.stub);
},
show: function(thread) {
@ -836,6 +910,9 @@
ReplyHiding.hide(this);
}
}
if (!Conf['Thread/Reply Hiding Buttons']) {
return;
}
return $.replace($('.sideArrows', this.nodes.root), ReplyHiding.makeButton(this, 'hide'));
},
getHiddenPosts: function() {
@ -946,6 +1023,9 @@
className: 'stub'
});
$.add(post.nodes.stub, a);
if (Conf['Menu']) {
$.add(post.nodes.stub, [$.tn(' '), Menu.makeButton(post)]);
}
return $.prepend(post.nodes.root, post.nodes.stub);
},
show: function(post) {
@ -1027,13 +1107,11 @@
},
node: function() {
var a;
a = Menu.makeButton(this);
if (this.isClone) {
a = $('.menu-button', this.nodes.info);
a.setAttribute('data-clone', true);
$.on(a, 'click', Menu.toggle);
$.replace($('.menu-button', this.nodes.info), a);
return;
}
a = Menu.makeButton(this);
return $.add(this.nodes.info, [$.tn('\u00A0'), a]);
},
makeButton: function(post) {
@ -1044,6 +1122,9 @@
href: 'javascript:;'
});
a.setAttribute('data-postid', post.fullID);
if (post.isClone) {
a.setAttribute('data-clone', true);
}
$.on(a, 'click', Menu.toggle);
return a;
},
@ -1097,8 +1178,10 @@
},
insertEntry: function(entry, parent, post) {
var child, submenu, _i, _len, _ref;
if (!entry.open(post)) {
return;
if (typeof entry.open === 'function') {
if (!entry.open(post)) {
return;
}
}
$.add(parent, entry.el);
if (!entry.children) {
@ -1166,7 +1249,7 @@
},
focus: function(entry) {
var bottom, eRect, focused, left, right, sRect, style, submenu, top, _i, _len, _ref;
if (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) {
while (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) {
$.rmClass(focused, 'focused');
}
_ref = $$('.focused', entry);
@ -3503,7 +3586,14 @@
try {
ReportLink.init();
} catch (err) {
$.log(err, 'Report Link', err.stack);
$.log(err, 'Report Link');
}
}
if (Conf['Thread Hiding']) {
try {
ThreadHiding.menu.init();
} catch (err) {
$.log(err, 'Thread Hiding - Menu');
}
}
if (Conf['Delete Link']) {

View File

@ -2,6 +2,8 @@ alpha
- Mayhem
Added touch and multi-touch support for dragging windows.
The Thread Updater will pause when offline, and resume when online.
Added Thread & Post Hiding in the Menu, with individual settings.
Thread & Post Hiding Buttons can now be disabled in the settings.
Recursive Hiding will be automatically applied when manually hiding a post.
Fix Chrome's install warning that 4chan X would execute on all domains.
Fix Quote Backlinks not affecting inlined quotes.

View File

@ -15,8 +15,9 @@ Config =
'Anonymize': [false, 'Turn everyone Anonymous.']
'Filter': [true, 'Self-moderation placebo.']
'Recursive Hiding': [true, 'Filter replies of filtered posts, recursively.']
'Reply Hiding': [true, 'Hide single replies.']
'Thread Hiding': [true, 'Hide entire threads.']
'Reply Hiding': [true, 'Hide single replies.']
'Thread/Reply Hiding Buttons': [true, 'Make buttons to hide threads / replies, in addition to menu links.']
'Stubs': [true, 'Make stubs of hidden threads / replies.']
Imaging:
'Auto-GIF': [false, 'Animate GIF thumbnails.']

View File

@ -9,15 +9,16 @@ ThreadHiding =
cb: @node
node: ->
if @ID in ThreadHiding.hiddenThreads.threads
ThreadHiding.hide @
if data = ThreadHiding.hiddenThreads.threads[@]
ThreadHiding.hide @, data.makeStub
return unless Conf['Thread/Reply Hiding Buttons']
$.prepend @posts[@].nodes.root, ThreadHiding.makeButton @, 'hide'
getHiddenThreads: ->
hiddenThreads = $.get "hiddenThreads.#{g.BOARD}"
unless hiddenThreads
hiddenThreads =
threads: []
threads: {}
lastChecked: Date.now()
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
ThreadHiding.hiddenThreads = hiddenThreads
@ -25,28 +26,72 @@ ThreadHiding =
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
{threads} = ThreadHiding.hiddenThreads
# Add threads that were hidden in the catalog.
for threadID of hiddenThreadsOnCatalog
continue if threadID of threads
threads[threadID] = {}
# Remove threads that were un-hidden in the catalog.
for threadID of threads
continue if threadID of threads
delete threads[threadID]
$.set "hiddenThreads.#{g.BOARD}", ThreadHiding.hiddenThreads
clean: ->
{hiddenThreads} = ThreadHiding
{lastChecked} = hiddenThreads
{lastChecked} = hiddenThreads
hiddenThreads.lastChecked = now = Date.now()
return if lastChecked > now - $.DAY
unless hiddenThreads.threads.length
unless Object.keys(hiddenThreads.threads).length
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
return
$.ajax "//api.4chan.org/#{g.BOARD}/catalog.json", onload: ->
threads = []
threads = {}
for obj in JSON.parse @response
for thread in obj.threads
threads.push thread.no if thread.no in hiddenThreads.threads
if thread.no of hiddenThreads.threads
threads[thread.no] = hiddenThreads.threads[thread.no]
hiddenThreads.threads = threads
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
menu:
init: ->
return if g.VIEW isnt 'index'
div = $.el 'div',
className: 'hide-thread-link'
textContent: 'Hide thread'
option = $.el 'label',
innerHTML: "<input type=checkbox checked=#{Conf['Stubs']}> Make stub"
apply = $.el 'a',
textContent: 'Apply'
href: 'javascript:;'
$.on apply, 'click', ThreadHiding.menu.hide
Menu.addEntry
el: div
open: (post) ->
{thread} = post
if post.isReply or thread.isHidden
return false
ThreadHiding.menu.thread = thread
true
children: [{el: option}, {el: apply}]
hide: ->
makeStub = $('input', @parentNode).checked
{thread} = ThreadHiding.menu
ThreadHiding.hide thread, makeStub
ThreadHiding.saveHiddenState thread, makeStub
# need to save if we made a stub or not
Menu.close()
makeButton: (thread, type) ->
a = $.el 'a',
className: "#{type}-thread-button"
@ -55,21 +100,26 @@ ThreadHiding =
$.on a, 'click', -> ThreadHiding.toggle thread
a
toggle: (thread) ->
saveHiddenState: (thread, makeStub) ->
# Get fresh hidden threads.
hiddenThreads = ThreadHiding.getHiddenThreads()
hiddenThreads = ThreadHiding.getHiddenThreads()
hiddenThreadsCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {}
if thread.isHidden
ThreadHiding.show thread
hiddenThreads.threads.splice hiddenThreads.threads.indexOf(thread.ID), 1
delete hiddenThreadsCatalog[thread]
hiddenThreads.threads[thread] = {makeStub}
hiddenThreadsCatalog[thread] = true
else
ThreadHiding.hide thread
hiddenThreads.threads.push thread.ID
hiddenThreadsCatalog[thread] = true
delete hiddenThreads.threads[thread]
delete hiddenThreadsCatalog[thread]
$.set "hiddenThreads.#{g.BOARD}", hiddenThreads
localStorage.setItem "4chan-hide-t-#{g.BOARD}", JSON.stringify hiddenThreadsCatalog
toggle: (thread) ->
if thread.isHidden
ThreadHiding.show thread
else
ThreadHiding.hide thread
ThreadHiding.saveHiddenState thread
hide: (thread, makeStub=Conf['Stubs']) ->
return if thread.hidden
op = thread.posts[thread]
@ -96,8 +146,8 @@ ThreadHiding =
thread.stub = $.el 'div',
className: 'stub'
$.add thread.stub, a
# if Conf['Menu']
# $.add thread.stub, [$.tn(' '), Menu.makeButton()]
if Conf['Menu']
$.add thread.stub, [$.tn(' '), Menu.makeButton op]
$.before threadRoot, thread.stub
show: (thread) ->
@ -121,6 +171,7 @@ ReplyHiding =
if thread = ReplyHiding.hiddenPosts.threads[@thread]
if @ID in thread
ReplyHiding.hide @
return unless Conf['Thread/Reply Hiding Buttons']
$.replace $('.sideArrows', @nodes.root), ReplyHiding.makeButton @, 'hide'
getHiddenPosts: ->
@ -199,8 +250,8 @@ ReplyHiding =
post.nodes.stub = $.el 'div',
className: 'stub'
$.add post.nodes.stub, a
# if Conf['Menu']
# $.add post.nodes.stub, [$.tn(' '), Menu.makeButton()]
if Conf['Menu']
$.add post.nodes.stub, [$.tn(' '), Menu.makeButton post]
$.prepend post.nodes.root, post.nodes.stub
show: (post) ->
@ -254,12 +305,10 @@ Menu =
cb: @node
node: ->
if @isClone
a = $ '.menu-button', @nodes.info
a.setAttribute 'data-clone', true
$.on a, 'click', Menu.toggle
return
a = Menu.makeButton @
if @isClone
$.replace $('.menu-button', @nodes.info), a
return
$.add @nodes.info, [$.tn('\u00A0'), a]
makeButton: (post) ->
@ -268,6 +317,7 @@ Menu =
innerHTML: '[<span></span>]'
href: 'javascript:;'
a.setAttribute 'data-postid', post.fullID
a.setAttribute 'data-clone', true if post.isClone
$.on a, 'click', Menu.toggle
a
@ -329,7 +379,8 @@ Menu =
menu.focus()
insertEntry: (entry, parent, post) ->
return unless entry.open post
if typeof entry.open is 'function'
return unless entry.open post
$.add parent, entry.el
return unless entry.children
@ -379,7 +430,7 @@ Menu =
e.stopPropagation()
focus: (entry) ->
if focused = $.x 'parent::*/child::*[contains(@class,"focused")]', entry
while focused = $.x 'parent::*/child::*[contains(@class,"focused")]', entry
$.rmClass focused, 'focused'
for focused in $$ '.focused', entry
$.rmClass focused, 'focused'

View File

@ -286,6 +286,7 @@ Main =
className: 'reply'
innerHTML: '<div class=extra></div>'
$.ready Main.initHeaderReady
initHeaderReady: ->
header = Main.header
$.prepend d.body, header
@ -354,7 +355,14 @@ Main =
ReportLink.init()
catch err
# XXX handle error
$.log err, 'Report Link', err.stack
$.log err, 'Report Link'
if Conf['Thread Hiding']
try
ThreadHiding.menu.init()
catch err
# XXX handle error
$.log err, 'Thread Hiding - Menu'
if Conf['Delete Link']
try
@ -469,6 +477,7 @@ Main =
$.log err, 'Thread Updater'
$.ready Main.initFeaturesReady
initFeaturesReady: ->
if d.title is '4chan - 404 Not Found'
if Conf['404 Redirect'] and g.VIEW is 'thread'