Merge branch 'master' into v3

I really need to get back to this.
This commit is contained in:
Nicolas Stepien 2013-01-13 19:17:28 +01:00
commit cf55f670c0
11 changed files with 262 additions and 169 deletions

View File

@ -3,7 +3,7 @@
// @version 3.0.0
// @description Cross-browser userscript for maximum lurking on 4chan.
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
// @copyright 2012 Nicolas Stepien <stepien.nicolas@gmail.com>
// @copyright 2012-2013 Nicolas Stepien <stepien.nicolas@gmail.com>
// @license MIT; http://en.wikipedia.org/wiki/Mit_license
// @match *://boards.4chan.org/*
// @match *://images.4chan.org/*

View File

@ -3,7 +3,7 @@
// @version 3.0.0
// @description Cross-browser userscript for maximum lurking on 4chan.
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
// @copyright 2012 Nicolas Stepien <stepien.nicolas@gmail.com>
// @copyright 2012-2013 Nicolas Stepien <stepien.nicolas@gmail.com>
// @license MIT; http://en.wikipedia.org/wiki/Mit_license
// @match *://boards.4chan.org/*
// @match *://images.4chan.org/*
@ -20,11 +20,11 @@
// @icon https://github.com/MayhemYDG/4chan-x/raw/stable/img/icon.gif
// ==/UserScript==
/* 4chan X Alpha - Version 3.0.0 - 2012-10-25
/* 4chan X Alpha - Version 3.0.0 - 2013-01-13
* http://mayhemydg.github.com/4chan-x/
*
* Copyright (c) 2009-2011 James Campos <james.r.campos@gmail.com>
* Copyright (c) 2012 Nicolas Stepien <stepien.nicolas@gmail.com>
* Copyright (c) 2012-2013 Nicolas Stepien <stepien.nicolas@gmail.com>
* Licensed under the MIT license.
* https://github.com/MayhemYDG/4chan-x/blob/master/LICENSE
*
@ -161,6 +161,7 @@
},
updater: {
checkbox: {
'Beep': [false, 'Beep on new post to completely read thread.'],
'Auto Scroll': [false, 'Scroll updated posts into view. Only enabled at bottom of page.'],
'Scroll BG': [false, 'Auto-scroll background tabs.'],
'Auto Update': [true, 'Automatically fetch new posts.']
@ -571,6 +572,9 @@
open: function(url) {
return (GM_openInTab || window.open)(url, '_blank');
},
hidden: function() {
return d.hidden || d.oHidden || d.mozHidden || d.webkitHidden;
},
queueTask: (function() {
var execTask, taskChannel, taskQueue;
taskQueue = [];
@ -691,6 +695,8 @@
return "//archive.foolz.us/" + board + "/full_image/" + filename;
case 'u':
return "//nsfw.foolz.us/" + board + "/full_image/" + filename;
case 'po':
return "http://archive.thedarkcave.org/" + board + "/full_image/" + filename;
case 'ck':
case 'lit':
return "//fuuka.warosu.org/" + board + "/full_image/" + filename;
@ -700,7 +706,6 @@
case 'cgl':
case 'g':
case 'mu':
case 'soc':
case 'w':
return "//rbt.asia/" + board + "/full_image/" + filename;
case 'an':
@ -711,6 +716,8 @@
case 'toy':
case 'x':
return "http://archive.heinessen.com/" + board + "/full_image/" + filename;
case 'c':
return "//archive.nyafuu.org/" + board + "/full_image/" + filename;
}
},
post: function(board, postID) {
@ -732,6 +739,8 @@
case 'u':
case 'kuku':
return "//nsfw.foolz.us/_/api/chan/post/?board=" + board + "&num=" + postID;
case 'po':
return "http://archive.thedarkcave.org/_/api/chan/post/?board=" + board + "&num=" + postID;
}
},
to: function(data) {
@ -757,6 +766,9 @@
case 'kuku':
url = Redirect.path('//nsfw.foolz.us', 'foolfuuka', data);
break;
case 'po':
url = Redirect.path('http://archive.thedarkcave.org', 'foolfuuka', data);
break;
case 'ck':
case 'lit':
url = Redirect.path('//fuuka.warosu.org', 'fuuka', data);
@ -768,7 +780,6 @@
case 'cgl':
case 'g':
case 'mu':
case 'soc':
case 'w':
url = Redirect.path('//rbt.asia', 'fuuka', data);
break;
@ -781,6 +792,9 @@
case 'x':
url = Redirect.path('http://archive.heinessen.com', 'fuuka', data);
break;
case 'c':
url = Redirect.path('//archive.nyafuu.org', 'fuuka', data);
break;
default:
if (data.threadID) {
url = "//boards.4chan.org/" + board + "/";
@ -807,6 +821,9 @@
postID = postID.match(/\d+/)[0];
}
path = threadID ? "" + board + "/thread/" + threadID : "" + board + "/post/" + postID;
if (archiver === 'foolfuuka') {
path += '/';
}
if (threadID && postID) {
path += archiver === 'foolfuuka' ? "#" + postID : "#p" + postID;
}
@ -835,7 +852,7 @@
capcode: data.capcode,
tripcode: data.trip,
uniqueID: data.id,
email: data.email ? encodeURIComponent(data.email.replace(/&quot;/g, '"')) : '',
email: data.email ? encodeURI(data.email.replace(/&quot;/g, '"')) : '',
subject: data.sub,
flagCode: data.country,
flagName: data.country_name,
@ -1111,7 +1128,7 @@
return '</b>';
}
});
comment = bq.innerHTML.replace(/(^|>)(&gt;[^<$]+)(<|$)/g, '$1<span class=quote>$2</span>$3');
comment = bq.innerHTML.replace(/(^|>)(&gt;[^<$]*)(<|$)/g, '$1<span class=quote>$2</span>$3').replace(/((&gt;){2}(&gt;\/[a-z\d]+\/)?\d+)/g, '<span class=deadlink>$1</span>');
threadID = data.thread_num;
o = {
postID: "" + postID,
@ -1130,7 +1147,7 @@
})(),
tripcode: data.trip,
uniqueID: data.poster_hash,
email: data.email ? encodeURIComponent(data.email) : '',
email: data.email ? encodeURI(data.email) : '',
subject: data.title_processed,
flagCode: data.poster_country,
flagName: data.poster_country_name_processed,
@ -1171,79 +1188,73 @@
});
},
node: function() {
var ID, a, board, data, i, index, m, node, nodes, post, quote, quoteID, quotes, snapshot, text, _i, _j, _len, _ref;
var ID, a, board, deadlink, m, post, quote, quoteID, redirect, _i, _len, _ref, _ref1;
if (this.isClone) {
return;
}
snapshot = d.evaluate('.//text()[not(parent::a)]', this.nodes.comment, null, 6, null);
for (i = _i = 0, _ref = snapshot.snapshotLength; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
node = snapshot.snapshotItem(i);
data = node.data;
if (!(quotes = data.match(/>>(>\/[a-z\d]+\/)?\d+/g))) {
_ref = $$('.deadlink', post.blockquote);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
deadlink = _ref[_i];
if (deadlink.parentNode.className === 'prettyprint') {
$.replace(deadlink, Array.prototype.slice.call(deadlink.childNodes));
continue;
}
nodes = [];
for (_j = 0, _len = quotes.length; _j < _len; _j++) {
quote = quotes[_j];
index = data.indexOf(quote);
if (text = data.slice(0, index)) {
nodes.push($.tn(text));
}
ID = quote.match(/\d+$/)[0];
board = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : this.board.ID;
quoteID = "" + board + "." + ID;
if (post = g.posts[quoteID]) {
if (post.isDead) {
a = $.el('a', {
href: Redirect.to({
board: board,
threadID: 0,
postID: ID
}),
className: 'quotelink deadlink',
textContent: "" + quote + "\u00A0(Dead)",
target: '_blank'
});
a.setAttribute('data-board', board);
a.setAttribute('data-threadid', post.thread.ID);
a.setAttribute('data-postid', ID);
} else {
a = $.el('a', {
href: "/" + board + "/" + post.thread + "/res/#p" + ID,
className: 'quotelink',
textContent: quote
});
}
} else {
quote = deadlink.textContent;
if (!(ID = (_ref1 = quote.match(/\d+$/)) != null ? _ref1[0] : void 0)) {
continue;
}
board = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : this.board.ID;
quoteID = "" + board + "." + ID;
if (post = g.posts[quoteID]) {
if (!post.isDead) {
a = $.el('a', {
href: Redirect.to({
board: board,
threadID: 0,
postID: ID
}),
className: 'deadlink',
href: "/" + board + "/" + post.thread + "/res/#p" + ID,
className: 'quotelink',
textContent: quote
});
} else if (redirect = Redirect.to({
board: board,
threadID: post.thread.ID,
postID: ID
})) {
a = $.el('a', {
href: redirect,
className: 'quotelink deadlink',
target: '_blank',
textContent: "" + quote + "\u00A0(Dead)"
});
if (Redirect.post(board, ID)) {
$.addClass(a, 'quotelink');
a.setAttribute('data-board', board);
a.setAttribute('data-postid', ID);
}
a.setAttribute('data-board', board);
a.setAttribute('data-threadid', post.thread.ID);
a.setAttribute('data-postid', ID);
}
if (this.quotes.indexOf(quoteID) === -1) {
this.quotes.push(quoteID);
} else if (redirect = Redirect.to({
board: board,
threadID: 0,
postID: ID
})) {
a = $.el('a', {
href: redirect,
className: 'deadlink',
target: '_blank',
textContent: "" + quote + "\u00A0(Dead)"
});
if (Redirect.post(board, ID)) {
$.addClass(a, 'quotelink');
a.setAttribute('data-board', board);
a.setAttribute('data-postid', ID);
}
if ($.hasClass(a, 'quotelink')) {
this.nodes.quotelinks.push(a);
}
nodes.push(a);
data = data.slice(index + quote.length);
}
if (data) {
nodes.push($.tn(data));
if (a) {
$.replace(deadlink, a);
} else {
deadlink.textContent += "\u00A0(Dead)";
}
if (this.quotes.indexOf(quoteID) === -1) {
this.quotes.push(quoteID);
}
if ($.hasClass(a, 'quotelink')) {
this.nodes.quotelinks.push(a);
}
$.replace(node, nodes);
}
}
};
@ -1988,6 +1999,12 @@
node: function() {
return new ThreadUpdater.Updater(this);
},
/*
http://freesound.org/people/pierrecartoons1979/sounds/90112/
cc-by-nc-3.0
*/
beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA',
Updater: (function() {
function _Class(thread) {
@ -2064,9 +2081,7 @@
}
},
visibility: function() {
var state;
state = d.visibilityState || d.oVisibilityState || d.mozVisibilityState || d.webkitVisibilityState;
if (state !== 'visible') {
if ($.hidden()) {
return;
}
this.unsuccessfulFetchCount = 0;
@ -2085,7 +2100,7 @@
return this.scrollBG = this['Scroll BG'] ? function() {
return true;
} : function() {
return !(d.hidden || d.oHidden || d.mozHidden || d.webkitHidden);
return !$.hidden();
};
},
autoUpdate: function() {
@ -2143,7 +2158,7 @@
var i, j;
i = this.interval;
j = Math.min(this.unsuccessfulFetchCount, 10);
if (!(d.hidden || d.oHidden || d.mozHidden || d.webkitHidden)) {
if (!$.hidden()) {
j = Math.min(j, 7);
}
return this.seconds = Math.max(i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]);
@ -2231,6 +2246,14 @@
}
}
if (count) {
if (Conf['Beep'] && $.hidden() && (Unread.replies.length === 0)) {
if (!this.audio) {
this.audio = $.el('audio', {
src: ThreadUpdater.beep
});
}
audio.play();
}
this.set('status', "+" + count);
this.status.className = 'new';
this.unsuccessfulFetchCount = 0;
@ -2299,7 +2322,7 @@
};
function Post(root, thread, board, that) {
var alt, anchor, bq, capcode, data, date, email, file, fileInfo, flag, i, info, name, node, nodes, post, quotelink, quotes, size, subject, text, thumb, tripcode, uniqueID, unit, _i, _j, _k, _len, _len1, _ref, _ref1, _ref2;
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;
this.thread = thread;
this.board = board;
if (that == null) {
@ -2365,13 +2388,22 @@
_ref2 = $$('.quotelink', this.nodes.comment);
for (_k = 0, _len1 = _ref2.length; _k < _len1; _k++) {
quotelink = _ref2[_k];
if (quotelink.hash) {
this.nodes.quotelinks.push(quotelink);
if (quotelink.parentNode.parentNode.className === 'capcodeReplies') {
continue;
}
quotes["" + (quotelink.pathname.split('/')[1]) + "." + quotelink.hash.slice(2)] = true;
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);
if ((file = $('.file', post)) && (thumb = $('img[data-md5]', file))) {

View File

@ -1,5 +1,5 @@
Copyright (c) 2009-2011 James Campos <james.r.campos@gmail.com>
Copyright (c) 2012 Nicolas Stepien <stepien.nicolas@gmail.com>
Copyright (c) 2012-2013 Nicolas Stepien <stepien.nicolas@gmail.com>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation

View File

@ -7,6 +7,47 @@ alpha
Fix Quote Highlighting not affecting inlined quotes.
master
- Mayhem
Add /po/ archive redirection for threads, images and post resurrection.
2.37.3
- Mayhem
Fix successful posting causing errors.
Fix 4chan X trying to interact with >>>/board/rules links.
2.37.2
- aeosynth
Beep on new post to completely read thread
- Mayhem
Fix dead quotes.
2.37.1
- noface
Fix Anonymize not working on stubs.
- Mayhem
Fix selection quoting on Opera.
Fix history bug with Persistent QR enabled on Chrome.
Fix posting warning not displaying the reason.
Fix deadquotes showing up in code-tags.
2.37.0
- noface
Add Catalog Links toggle.
Fix Anonymize not working on hovered posts.
- Mayhem
Added catalog support.
Sync thread hiding between index and catalog.
Add /c/ archived thread and image redirection.
2.36.3
- Mayhem
Fix next/previous page keybinds.
2.36.2
- noface
Add tags support on /f/.
- Mayhem
Add /mu/ archived image redirection.
2.36.1
- noface

View File

@ -1 +1 @@
postMessage({version:'2.36.1'},'*')
postMessage({version:'2.37.3'},'*')

View File

@ -138,6 +138,8 @@ $.extend $,
return
open: (url) ->
(GM_openInTab or window.open) url, '_blank'
hidden: ->
d.hidden or d.oHidden or d.mozHidden or d.webkitHidden
queueTask: (->
# inspired by https://www.w3.org/Bugs/Public/show_bug.cgi?id=15007
taskQueue = []

View File

@ -2,7 +2,7 @@
* http://mayhemydg.github.com/4chan-x/
*
* Copyright (c) 2009-2011 James Campos <james.r.campos@gmail.com>
* Copyright (c) <%= grunt.template.today('yyyy') %> Nicolas Stepien <stepien.nicolas@gmail.com>
* Copyright (c) 2012-<%= grunt.template.today('yyyy') %> Nicolas Stepien <stepien.nicolas@gmail.com>
* Licensed under the MIT license.
* <%= meta.repo %>blob/master/LICENSE
*

View File

@ -156,6 +156,7 @@ Config =
'hide': ['x', 'Hide thread.']
updater:
checkbox:
'Beep': [false, 'Beep on new post to completely read thread.']
'Auto Scroll': [false, 'Scroll updated posts into view. Only enabled at bottom of page.']
'Scroll BG': [false, 'Auto-scroll background tabs.']
'Auto Update': [true, 'Automatically fetch new posts.']

View File

@ -7,20 +7,26 @@ Redirect =
"//archive.foolz.us/#{board}/full_image/#{filename}"
when 'u'
"//nsfw.foolz.us/#{board}/full_image/#{filename}"
when 'po'
"http://archive.thedarkcave.org/#{board}/full_image/#{filename}"
when 'ck', 'lit'
"//fuuka.warosu.org/#{board}/full_image/#{filename}"
when 'diy', 'sci'
"//archive.installgentoo.net/#{board}/full_image/#{filename}"
when 'cgl', 'g', 'mu', 'soc', 'w'
when 'cgl', 'g', 'mu', 'w'
"//rbt.asia/#{board}/full_image/#{filename}"
when 'an', 'fit', 'k', 'mlp', 'r9k', 'toy', 'x'
"http://archive.heinessen.com/#{board}/full_image/#{filename}"
when 'c'
"//archive.nyafuu.org/#{board}/full_image/#{filename}"
post: (board, postID) ->
switch board
when 'a', 'co', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'wsg', 'dev', 'foolz'
"//archive.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}"
when 'u', 'kuku'
"//nsfw.foolz.us/_/api/chan/post/?board=#{board}&num=#{postID}"
when 'po'
"http://archive.thedarkcave.org/_/api/chan/post/?board=#{board}&num=#{postID}"
# for fuuka-based archives:
# https://github.com/eksopl/fuuka/issues/27
to: (data) ->
@ -30,14 +36,18 @@ Redirect =
url = Redirect.path '//archive.foolz.us', 'foolfuuka', data
when 'u', 'kuku'
url = Redirect.path '//nsfw.foolz.us', 'foolfuuka', data
when 'po'
url = Redirect.path 'http://archive.thedarkcave.org', 'foolfuuka', data
when 'ck', 'lit'
url = Redirect.path '//fuuka.warosu.org', 'fuuka', data
when 'diy', 'sci'
url = Redirect.path '//archive.installgentoo.net', 'fuuka', data
when 'cgl', 'g', 'mu', 'soc', 'w'
when 'cgl', 'g', 'mu', 'w'
url = Redirect.path '//rbt.asia', 'fuuka', data
when 'an', 'fit', 'k', 'mlp', 'r9k', 'toy', 'x'
url = Redirect.path 'http://archive.heinessen.com', 'fuuka', data
when 'c'
url = Redirect.path '//archive.nyafuu.org', 'fuuka', data
else
if data.threadID
url = "//boards.4chan.org/#{board}/"
@ -68,6 +78,8 @@ Redirect =
"#{board}/thread/#{threadID}"
else
"#{board}/post/#{postID}"
if archiver is 'foolfuuka'
path += '/'
if threadID and postID
path +=
if archiver is 'foolfuuka'
@ -98,7 +110,7 @@ Build =
capcode: data.capcode
tripcode: data.trip
uniqueID: data.id
email: if data.email then encodeURIComponent data.email.replace /&quot;/g, '"' else ''
email: if data.email then encodeURI data.email.replace /&quot;/g, '"' else ''
subject: data.sub
flagCode: data.country
flagName: data.country_name
@ -474,8 +486,12 @@ Get =
'<b style="color: red;">'
when '[/banned]'
'</b>'
# greentext
comment = bq.innerHTML.replace /(^|>)(&gt;[^<$]+)(<|$)/g, '$1<span class=quote>$2</span>$3'
comment = bq.innerHTML
# greentext
.replace(/(^|>)(&gt;[^<$]*)(<|$)/g, '$1<span class=quote>$2</span>$3')
# quotes
.replace /((&gt;){2}(&gt;\/[a-z\d]+\/)?\d+)/g, '<span class=deadlink>$1</span>'
threadID = data.thread_num
o =
@ -491,7 +507,7 @@ Get =
when 'D' then 'developer'
tripcode: data.trip
uniqueID: data.poster_hash
email: if data.email then encodeURIComponent data.email else ''
email: if data.email then encodeURI data.email else ''
subject: data.title_processed
flagCode: data.poster_country
flagName: data.poster_country_name_processed
@ -529,82 +545,63 @@ Quotify =
cb: @node
node: ->
return if @isClone
for deadlink in $$ '.deadlink', post.blockquote
if deadlink.parentNode.className is 'prettyprint'
# Don't quotify deadlinks inside code tags,
# un-`span` them.
$.replace deadlink, Array::slice.call deadlink.childNodes
continue
# XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE === 6
# Get all the text nodes that are not inside an anchor.
snapshot = d.evaluate './/text()[not(parent::a)]', @nodes.comment, null, 6, null
for i in [0...snapshot.snapshotLength]
node = snapshot.snapshotItem i
data = node.data
# Only accept nodes with potentially valid links
continue unless quotes = data.match />>(>\/[a-z\d]+\/)?\d+/g
nodes = []
for quote in quotes
index = data.indexOf quote
if text = data[...index]
# Potential text before this valid quote.
nodes.push $.tn text
ID = quote.match(/\d+$/)[0]
board =
if m = quote.match /^>>>\/([a-z\d]+)/
m[1]
else
@board.ID
quoteID = "#{board}.#{ID}"
# \u00A0 is nbsp
if post = g.posts[quoteID]
if post.isDead
a = $.el 'a',
href: Redirect.to
board: board
threadID: 0
postID: ID
className: 'quotelink deadlink'
textContent: "#{quote}\u00A0(Dead)"
target: '_blank'
a.setAttribute 'data-board', board
a.setAttribute 'data-threadid', post.thread.ID
a.setAttribute 'data-postid', ID
else
# Don't (Dead) when quotifying in an archived post,
# and we know the post still exists.
a = $.el 'a',
href: "/#{board}/#{post.thread}/res/#p#{ID}"
className: 'quotelink'
textContent: quote
quote = deadlink.textContent
continue unless ID = quote.match(/\d+$/)?[0]
board =
if m = quote.match /^>>>\/([a-z\d]+)/
m[1]
else
@board.ID
quoteID = "#{board}.#{ID}"
# \u00A0 is nbsp
if post = g.posts[quoteID]
unless post.isDead
# Don't (Dead) when quotifying in an archived post,
# and we know the post still exists.
a = $.el 'a',
href: Redirect.to
board: board
threadID: 0
postID: ID
className: 'deadlink'
href: "/#{board}/#{post.thread}/res/#p#{ID}"
className: 'quotelink'
textContent: quote
else if redirect = Redirect.to {board: board, threadID: post.thread.ID, postID: ID}
# Replace the .deadlink span if we can redirect.
a = $.el 'a',
href: redirect
className: 'quotelink deadlink'
target: '_blank'
textContent: "#{quote}\u00A0(Dead)"
if Redirect.post board, ID
$.addClass a, 'quotelink'
a.setAttribute 'data-board', board
a.setAttribute 'data-postid', ID
a.setAttribute 'data-board', board
a.setAttribute 'data-threadid', post.thread.ID
a.setAttribute 'data-postid', ID
else if redirect = Redirect.to {board: board, threadID: 0, postID: ID}
# Replace the .deadlink span if we can redirect.
a = $.el 'a',
href: redirect
className: 'deadlink'
target: '_blank'
textContent: "#{quote}\u00A0(Dead)"
if Redirect.post board, ID
# Make it function as a normal quote if we can fetch the post.
$.addClass a, 'quotelink'
a.setAttribute 'data-board', board
a.setAttribute 'data-postid', ID
if @quotes.indexOf(quoteID) is -1
@quotes.push quoteID
if $.hasClass a, 'quotelink'
@nodes.quotelinks.push a
nodes.push a
data = data[index + quote.length..]
if a
$.replace deadlink, a
else
deadlink.textContent += "\u00A0(Dead)"
if data
# Potential text after the last valid quote.
nodes.push $.tn data
$.replace node, nodes
if @quotes.indexOf(quoteID) is -1
@quotes.push quoteID
if $.hasClass a, 'quotelink'
@nodes.quotelinks.push a
return
QuoteInline =
@ -1109,6 +1106,12 @@ ThreadUpdater =
cb: @node
node: ->
new ThreadUpdater.Updater @
###
http://freesound.org/people/pierrecartoons1979/sounds/90112/
cc-by-nc-3.0
###
beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA'
Updater: class
constructor: (@thread) ->
@ -1175,8 +1178,7 @@ ThreadUpdater =
@unsuccessfulFetchCount = 0
setTimeout @update.bind(@), 1000 if @seconds > 2
visibility: ->
state = d.visibilityState or d.oVisibilityState or d.mozVisibilityState or d.webkitVisibilityState
return if state isnt 'visible'
return if $.hidden()
# Reset the counter when we focus this tab.
@unsuccessfulFetchCount = 0
if @seconds > @interval
@ -1191,7 +1193,7 @@ ThreadUpdater =
if @['Scroll BG']
-> true
else
-> !(d.hidden or d.oHidden or d.mozHidden or d.webkitHidden)
-> not $.hidden()
autoUpdate: ->
if @['Auto Update This'] and @online
@timeoutID = setTimeout @timeout.bind(@), 1000
@ -1241,7 +1243,7 @@ ThreadUpdater =
getInterval: ->
i = @interval
j = Math.min @unsuccessfulFetchCount, 10
unless d.hidden or d.oHidden or d.mozHidden or d.webkitHidden
unless $.hidden()
# Lower the max refresh rate limit on visible tabs.
j = Math.min j, 7
@seconds = Math.max i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]
@ -1308,6 +1310,10 @@ ThreadUpdater =
post.kill true
if count
if Conf['Beep'] and $.hidden() and (Unread.replies.length is 0)
unless @audio
@audio = $.el 'audio', src: ThreadUpdater.beep
audio.play()
@set 'status', "+#{count}"
@status.className = 'new'
@unsuccessfulFetchCount = 0

View File

@ -88,13 +88,24 @@ class Post
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: ...)
# Only add quotes that link to posts on an imageboard.
if quotelink.hash
@nodes.quotelinks.push quotelink
continue if quotelink.parentNode.parentNode.className is 'capcodeReplies'
quotes["#{quotelink.pathname.split('/')[1]}.#{quotelink.hash[2..]}"] = true
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
if (file = $ '.file', post) and thumb = $ 'img[data-md5]', file

View File

@ -3,7 +3,7 @@
// @version <%= pkg.version %>
// @description Cross-browser userscript for maximum lurking on 4chan.
// @copyright 2009-2011 James Campos <james.r.campos@gmail.com>
// @copyright <%= grunt.template.today('yyyy') %> Nicolas Stepien <stepien.nicolas@gmail.com>
// @copyright 2012-<%= grunt.template.today('yyyy') %> Nicolas Stepien <stepien.nicolas@gmail.com>
// @license MIT; http://en.wikipedia.org/wiki/Mit_license
// @match *://boards.4chan.org/*
// @match *://images.4chan.org/*