diff --git a/4chan_x.meta.js b/4chan_x.meta.js index 053d36de9..81b68e9d3 100644 --- a/4chan_x.meta.js +++ b/4chan_x.meta.js @@ -3,7 +3,7 @@ // @version 3.0.0 // @description Cross-browser userscript for maximum lurking on 4chan. // @copyright 2009-2011 James Campos -// @copyright 2012 Nicolas Stepien +// @copyright 2012-2013 Nicolas Stepien // @license MIT; http://en.wikipedia.org/wiki/Mit_license // @match *://boards.4chan.org/* // @match *://images.4chan.org/* diff --git a/4chan_x.user.js b/4chan_x.user.js index 1803483cd..f4a79690a 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -3,7 +3,7 @@ // @version 3.0.0 // @description Cross-browser userscript for maximum lurking on 4chan. // @copyright 2009-2011 James Campos -// @copyright 2012 Nicolas Stepien +// @copyright 2012-2013 Nicolas Stepien // @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 - * Copyright (c) 2012 Nicolas Stepien + * Copyright (c) 2012-2013 Nicolas Stepien * 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(/"/g, '"')) : '', + email: data.email ? encodeURI(data.email.replace(/"/g, '"')) : '', subject: data.sub, flagCode: data.country, flagName: data.country_name, @@ -1111,7 +1128,7 @@ return ''; } }); - comment = bq.innerHTML.replace(/(^|>)(>[^<$]+)(<|$)/g, '$1$2$3'); + comment = bq.innerHTML.replace(/(^|>)(>[^<$]*)(<|$)/g, '$1$2$3').replace(/((>){2}(>\/[a-z\d]+\/)?\d+)/g, '$1'); 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))) { diff --git a/LICENSE b/LICENSE index a8936987a..37991acde 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ Copyright (c) 2009-2011 James Campos -Copyright (c) 2012 Nicolas Stepien +Copyright (c) 2012-2013 Nicolas Stepien Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/changelog b/changelog index 01529fcf5..14c3cd712 100644 --- a/changelog +++ b/changelog @@ -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 diff --git a/latest.js b/latest.js index a000e475e..dda29cbcb 100644 --- a/latest.js +++ b/latest.js @@ -1 +1 @@ -postMessage({version:'2.36.1'},'*') \ No newline at end of file +postMessage({version:'2.37.3'},'*') \ No newline at end of file diff --git a/lib/$.coffee b/lib/$.coffee index 1a030a6f1..f1d9231ee 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -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 = [] diff --git a/src/banner.js b/src/banner.js index 394535c23..8e39fc643 100644 --- a/src/banner.js +++ b/src/banner.js @@ -2,7 +2,7 @@ * http://mayhemydg.github.com/4chan-x/ * * Copyright (c) 2009-2011 James Campos - * Copyright (c) <%= grunt.template.today('yyyy') %> Nicolas Stepien + * Copyright (c) 2012-<%= grunt.template.today('yyyy') %> Nicolas Stepien * Licensed under the MIT license. * <%= meta.repo %>blob/master/LICENSE * diff --git a/src/config.coffee b/src/config.coffee index 1349f6c6c..c924f4c0e 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -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.'] diff --git a/src/features.coffee b/src/features.coffee index 43447b65a..9b6f62e43 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -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 /"/g, '"' else '' + email: if data.email then encodeURI data.email.replace /"/g, '"' else '' subject: data.sub flagCode: data.country flagName: data.country_name @@ -474,8 +486,12 @@ Get = '' when '[/banned]' '' - # greentext - comment = bq.innerHTML.replace /(^|>)(>[^<$]+)(<|$)/g, '$1$2$3' + + comment = bq.innerHTML + # greentext + .replace(/(^|>)(>[^<$]*)(<|$)/g, '$1$2$3') + # quotes + .replace /((>){2}(>\/[a-z\d]+\/)?\d+)/g, '$1' 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 diff --git a/src/main.coffee b/src/main.coffee index 4b53879bf..9682e5b96 100644 --- a/src/main.coffee +++ b/src/main.coffee @@ -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 diff --git a/src/metadata.js b/src/metadata.js index 796b1dddc..d7466179e 100644 --- a/src/metadata.js +++ b/src/metadata.js @@ -3,7 +3,7 @@ // @version <%= pkg.version %> // @description Cross-browser userscript for maximum lurking on 4chan. // @copyright 2009-2011 James Campos -// @copyright <%= grunt.template.today('yyyy') %> Nicolas Stepien +// @copyright 2012-<%= grunt.template.today('yyyy') %> Nicolas Stepien // @license MIT; http://en.wikipedia.org/wiki/Mit_license // @match *://boards.4chan.org/* // @match *://images.4chan.org/*