From 75f41069adce6c31312323c76f7e48ff8d07cf13 Mon Sep 17 00:00:00 2001 From: ahodesuka Date: Mon, 20 Feb 2012 00:08:37 -0600 Subject: [PATCH 01/28] Add an option to only expand images which have not been scrolled past when using the expand all button. --- 4chan_x.user.js | 9 ++++++++- script.coffee | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index e365c8737..96bc0d33b 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -100,6 +100,7 @@ Imaging: { 'Image Auto-Gif': [false, 'Animate gif thumbnails'], 'Image Expansion': [true, 'Expand images'], + 'Expand All (Unscrolled)': [false, 'Expand all images will only expand those which you have not scrolled past'], 'Image Hover': [false, 'Show full image on mouseover'], 'Sauce': [true, 'Add sauce to images'], 'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail'] @@ -3392,7 +3393,13 @@ _ref = $$('img[md5]'); for (_i = 0, _len = _ref.length; _i < _len; _i++) { thumb = _ref[_i]; - imgExpand.expand(thumb); + if (conf['Expand All (Unscrolled)']) { + if (thumb.getBoundingClientRect().bottom >= 0) { + imgExpand.expand(thumb); + } + } else { + imgExpand.expand(thumb); + } } } else { _ref2 = $$('img[md5][hidden]'); diff --git a/script.coffee b/script.coffee index 0b8a6c988..0227df801 100644 --- a/script.coffee +++ b/script.coffee @@ -22,6 +22,7 @@ config = Imaging: 'Image Auto-Gif': [false, 'Animate gif thumbnails'] 'Image Expansion': [true, 'Expand images'] + 'Expand All (Unscrolled)': [false, 'Expand all images will only expand those which you have not scrolled past'] 'Image Hover': [false, 'Show full image on mouseover'] 'Sauce': [true, 'Add sauce to images'] 'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail'] @@ -2663,7 +2664,11 @@ imgExpand = imgExpand.on = @checked if imgExpand.on #expand for thumb in $$ 'img[md5]' - imgExpand.expand thumb + if conf['Expand All (Unscrolled)'] + if thumb.getBoundingClientRect().bottom >= 0 + imgExpand.expand thumb + else + imgExpand.expand thumb else #contract for thumb in $$ 'img[md5][hidden]' imgExpand.contract thumb From 734eeae827e277aef3ebac59143919e5f0ce3f78 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 20 Feb 2012 18:15:47 +0100 Subject: [PATCH 02/28] Fix quoting the OP didn't select the thread ID. --- 4chan_x.user.js | 2 +- script.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index e365c8737..b523ccaa5 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1379,7 +1379,7 @@ if (e != null) e.preventDefault(); qr.open(); if (!g.REPLY) { - $('select', qr.el).value = $.x('ancestor::div', this).firstChild.id; + $('select', qr.el).value = $.x('ancestor::div[@class="thread"]', this).firstChild.id; } id = this.previousElementSibling.hash.slice(1); text = ">>" + id + "\n"; diff --git a/script.coffee b/script.coffee index 0b8a6c988..99dddba86 100644 --- a/script.coffee +++ b/script.coffee @@ -1023,7 +1023,7 @@ qr = e?.preventDefault() qr.open() unless g.REPLY - $('select', qr.el).value = $.x('ancestor::div', @).firstChild.id + $('select', qr.el).value = $.x('ancestor::div[@class="thread"]', @).firstChild.id # Make sure we get the correct number, even with XXX censors id = @previousElementSibling.hash[1..] From d4bb30740415e6b4314091dd01ab865a15767324 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 20 Feb 2012 18:20:55 +0100 Subject: [PATCH 03/28] Fix #250. --- 4chan_x.user.js | 7 ++++--- script.coffee | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index b523ccaa5..a4b74be2c 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -3099,18 +3099,19 @@ return g.callbacks.push(this.node); }, node: function(root) { - var path, quote, tid, _i, _len, _ref; + var hash, path, quote, tid, _i, _len, _ref; if (root.className === 'inline') return; tid = g.THREAD_ID || $.x('ancestor::div[contains(@class,"thread")]', root).firstChild.id; _ref = $$('.quotelink', root); for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; - if (conf['Indicate OP quote'] && quote.hash.slice(1) === tid) { + hash = quote.hash.slice(1); + if (conf['Indicate OP quote'] && hash === tid) { $.add(quote, $.tn('\u00A0(OP)')); return; } path = quote.pathname; - if (conf['Indicate Cross-thread Quotes'] && path.lastIndexOf("/" + tid) === -1 && path.indexOf("/" + g.BOARD + "/") === 0) { + if (conf['Indicate Cross-thread Quotes'] && hash && path.lastIndexOf("/" + tid) === -1 && path.indexOf("/" + g.BOARD + "/") === 0) { $.add(quote, $.tn('\u00A0(Cross-thread)')); } } diff --git a/script.coffee b/script.coffee index 99dddba86..d2b2d593a 100644 --- a/script.coffee +++ b/script.coffee @@ -2441,13 +2441,14 @@ quoteIndicators = # We use contains() so that it works with hidden threads tid = g.THREAD_ID or $.x('ancestor::div[contains(@class,"thread")]', root).firstChild.id for quote in $$ '.quotelink', root - if conf['Indicate OP quote'] and quote.hash[1..] is tid + hash = quote.hash[1..] + if conf['Indicate OP quote'] and hash is tid # \u00A0 is nbsp $.add quote, $.tn '\u00A0(OP)' return path = quote.pathname #if quote leads to a different thread id and is located on the same board (index 0) - if conf['Indicate Cross-thread Quotes'] and path.lastIndexOf("/#{tid}") is -1 and path.indexOf("/#{g.BOARD}/") is 0 + if conf['Indicate Cross-thread Quotes'] and hash and path.lastIndexOf("/#{tid}") is -1 and path.indexOf("/#{g.BOARD}/") is 0 # \u00A0 is nbsp $.add quote, $.tn '\u00A0(Cross-thread)' return From 97902bbfcc6fdbeab24c462484593dfd7c8cd45b Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 20 Feb 2012 21:36:43 +0100 Subject: [PATCH 04/28] Fix #248. --- 4chan_x.user.js | 4 ++-- changelog | 2 ++ script.coffee | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index a4b74be2c..89fe9fbf3 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1223,7 +1223,7 @@ $.before(form, link); } g.callbacks.push(this.node); - if (engine === 'webkit') { + if (/chrome/i.test(navigator.userAgent)) { qr.status({ ready: true }); @@ -1832,7 +1832,7 @@ reader.readAsBinaryString(reply.file); return; } - if (engine === 'webkit') { + if (/chrome/i.test(navigator.userAgent)) { qr.message.post(post); return; } diff --git a/changelog b/changelog index 6560e094b..496914583 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master +- Mayhem + Fix posting on Safari. 2.26.4 - Mayhem diff --git a/script.coffee b/script.coffee index d2b2d593a..57b15dd6d 100644 --- a/script.coffee +++ b/script.coffee @@ -900,7 +900,8 @@ qr = $.before form, link g.callbacks.push @node - if engine is 'webkit' + # CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox. + if /chrome/i.test navigator.userAgent qr.status ready: true else iframe = $.el 'iframe', @@ -1405,7 +1406,8 @@ qr = reader.readAsBinaryString reply.file return - if engine is 'webkit' + # CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox. + if /chrome/i.test navigator.userAgent qr.message.post post return qr.message.send post From e769c56a9fe448ae81401d06b84544d956458616 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 20 Feb 2012 22:17:13 +0100 Subject: [PATCH 05/28] Hopefuly last Safari posting bug fix. #248 --- 4chan_x.user.js | 2 +- script.coffee | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 89fe9fbf3..775081f63 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1899,7 +1899,7 @@ message: { send: function(data) { var host, window; - if (engine === 'webkit') { + if (/chrome/i.test(navigator.userAgent)) { qr.message.receive(data); return; } diff --git a/script.coffee b/script.coffee index 57b15dd6d..233be78fc 100644 --- a/script.coffee +++ b/script.coffee @@ -1469,7 +1469,8 @@ qr = message: send: (data) -> - if engine is 'webkit' + # CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox. + if /chrome/i.test navigator.userAgent qr.message.receive data return data.qr = true From 2b62ce1231b6f5b1eb8734f1c2450373dbcba00c Mon Sep 17 00:00:00 2001 From: James Campos Date: Mon, 20 Feb 2012 14:16:48 -0800 Subject: [PATCH 06/28] build on ahodesuka's work --- 4chan_x.user.js | 29 +++++++++++++++-------------- script.coffee | 16 +++++++++------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 14d46fec2..341bf5cdb 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -100,7 +100,7 @@ Imaging: { 'Image Auto-Gif': [false, 'Animate gif thumbnails'], 'Image Expansion': [true, 'Expand images'], - 'Expand All (Unscrolled)': [false, 'Expand all images will only expand those which you have not scrolled past'], + 'Expand From Current': [true, 'Expand images from current position to thread end'], 'Image Hover': [false, 'Show full image on mouseover'], 'Sauce': [true, 'Add sauce to images'], 'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail'] @@ -3388,24 +3388,25 @@ return imgExpand.toggle(this); }, all: function() { - var thumb, _i, _j, _len, _len2, _ref, _ref2; + var i, thumb, thumbs, _i, _j, _len, _len2, _len3, _ref; imgExpand.on = this.checked; if (imgExpand.on) { - _ref = $$('img[md5]'); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - thumb = _ref[_i]; - if (conf['Expand All (Unscrolled)']) { - if (thumb.getBoundingClientRect().bottom >= 0) { - imgExpand.expand(thumb); - } - } else { - imgExpand.expand(thumb); + thumbs = $$('img[md5]'); + if (conf['Expand From Current']) { + for (i = 0, _len = thumbs.length; i < _len; i++) { + thumb = thumbs[i]; + if (thumb.getBoundingClientRect().top > 0) break; } + thumbs = thumbs.slice(i); + } + for (_i = 0, _len2 = thumbs.length; _i < _len2; _i++) { + thumb = thumbs[_i]; + imgExpand.expand(thumb); } } else { - _ref2 = $$('img[md5][hidden]'); - for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { - thumb = _ref2[_j]; + _ref = $$('img[md5][hidden]'); + for (_j = 0, _len3 = _ref.length; _j < _len3; _j++) { + thumb = _ref[_j]; imgExpand.contract(thumb); } } diff --git a/script.coffee b/script.coffee index a4ea526a9..c7c429310 100644 --- a/script.coffee +++ b/script.coffee @@ -22,7 +22,7 @@ config = Imaging: 'Image Auto-Gif': [false, 'Animate gif thumbnails'] 'Image Expansion': [true, 'Expand images'] - 'Expand All (Unscrolled)': [false, 'Expand all images will only expand those which you have not scrolled past'] + 'Expand From Current': [true, 'Expand images from current position to thread end'] 'Image Hover': [false, 'Show full image on mouseover'] 'Sauce': [true, 'Add sauce to images'] 'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail'] @@ -2664,12 +2664,14 @@ imgExpand = all: -> imgExpand.on = @checked if imgExpand.on #expand - for thumb in $$ 'img[md5]' - if conf['Expand All (Unscrolled)'] - if thumb.getBoundingClientRect().bottom >= 0 - imgExpand.expand thumb - else - imgExpand.expand thumb + thumbs = $$ 'img[md5]' + if conf['Expand From Current'] + for thumb, i in thumbs + if thumb.getBoundingClientRect().top > 0 + break + thumbs = thumbs[i...] + for thumb in thumbs + imgExpand.expand thumb else #contract for thumb in $$ 'img[md5][hidden]' imgExpand.contract thumb From a77191821bc9fd8eecbfda4cac8de19bae006f55 Mon Sep 17 00:00:00 2001 From: James Campos Date: Mon, 20 Feb 2012 14:22:59 -0800 Subject: [PATCH 07/28] expandImage, singular --- 4chan_x.user.js | 4 ++-- script.coffee | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 341bf5cdb..f1bcab6b6 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -169,7 +169,7 @@ expandThread: ['e', 'Expand thread'], watch: ['w', 'Watch thread'], hide: ['x', 'Hide thread'], - expandImages: ['m', 'Expand selected image'], + expandImage: ['m', 'Expand selected image'], expandAllImages: ['M', 'Expand all images'], update: ['u', 'Update now'], unreadCountTo0: ['z', 'Reset unread status'] @@ -933,7 +933,7 @@ case conf.expandThread: expandThread.toggle(thread); break; - case conf.expandImages: + case conf.expandImage: keybinds.img(thread); break; case conf.nextThread: diff --git a/script.coffee b/script.coffee index c7c429310..fab4742ab 100644 --- a/script.coffee +++ b/script.coffee @@ -98,7 +98,7 @@ config = expandThread: ['e', 'Expand thread'] watch: ['w', 'Watch thread'] hide: ['x', 'Hide thread'] - expandImages: ['m', 'Expand selected image'] + expandImage: ['m', 'Expand selected image'] expandAllImages: ['M', 'Expand all images'] update: ['u', 'Update now'] unreadCountTo0: ['z', 'Reset unread status'] @@ -699,7 +699,7 @@ keybinds = keybinds.open thread when conf.expandThread expandThread.toggle thread - when conf.expandImages + when conf.expandImage keybinds.img thread when conf.nextThread return if g.REPLY From 28e403c47091b4f6948f68b5d24086374c68a9cc Mon Sep 17 00:00:00 2001 From: James Campos Date: Mon, 20 Feb 2012 14:23:41 -0800 Subject: [PATCH 08/28] changelog --- changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog b/changelog index 6560e094b..8007556dc 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master +- aeosynth / ahodesuka + expand images from current position 2.26.4 - Mayhem From c12538f2c79deb5ec883fa9538d103b1d33b0e3a Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 02:11:17 +0100 Subject: [PATCH 09/28] Filter improvements. Add flags: op (yes|no|only), per boards or global, highlight or hide. Remove Filter OPs option. Turn on Filter and Recursive Filtering by default. Close #18. --- 4chan_x.user.js | 124 +++++++++++++++++++++++++------------------ script.coffee | 137 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 171 insertions(+), 90 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index a4b74be2c..0a6448f69 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -90,9 +90,8 @@ }, Filtering: { 'Anonymize': [false, 'Make everybody anonymous'], - 'Filter': [false, 'Self-moderation placebo'], - 'Filter OPs': [false, 'Filter OPs along with their threads'], - 'Recursive Filtering': [false, 'Filter replies of filtered posts, recursively'], + 'Filter': [true, 'Self-moderation placebo'], + 'Recursive Filtering': [true, 'Filter replies of filtered posts, recursively'], 'Reply Hiding': [true, 'Hide single replies'], 'Thread Hiding': [true, 'Hide entire threads'], 'Show Stubs': [true, 'Of hidden threads / replies'] @@ -532,86 +531,108 @@ }; filter = { - regexps: {}, - callbacks: [], + filters: {}, init: function() { - var f, filter, key, m, _i, _len; + var boards, filter, hl, key, op, regexp, _i, _len, _ref, _ref2, _ref3; for (key in config.filter) { - if (!(m = conf[key].match(/^\/.+\/\w*$/gm))) continue; - this.regexps[key] = []; - for (_i = 0, _len = m.length; _i < _len; _i++) { - filter = m[_i]; - f = filter.match(/^\/(.+)\/(\w*)$/); + this.filters[key] = []; + _ref = conf[key].split('\n'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + filter = _ref[_i]; + if (filter[0] === '#') continue; + if (!(regexp = filter.match(/\/(.+)\/(\w*)/))) continue; + filter = filter.replace(regexp[0], ''); + boards = ((_ref2 = filter.match(/boards:([^;]+)/)) != null ? _ref2[1].toLowerCase() : void 0) || 'global'; + if (boards !== 'global' && boards.split(',').indexOf(g.BOARD) === -1) { + continue; + } try { - this.regexps[key].push(RegExp(f[1], f[2])); + regexp = RegExp(regexp[1], regexp[2]); } catch (e) { alert(e.message); + continue; } + op = ((_ref3 = filter.match(/op:(yes|no|only)/)) != null ? _ref3[1].toLowerCase() : void 0) || 'no'; + hl = /highlight/.test(filter); + this.filters[key].push(this.createFilter(regexp, op, hl)); } - this.callbacks.push(this[key]); + if (!this.filters[key].length) delete this.filters[key]; } - return g.callbacks.push(this.node); + if (Object.keys(this.filters).length) return g.callbacks.push(this.node); + }, + createFilter: function(regexp, op, hl) { + return function(root, value, isOP) { + if (isOP && op === 'no' || op === 'only') return false; + if (!regexp.test(value)) return false; + if (hl) { + $.addClass(root, 'filter_highlight'); + } else if (isOP) { + threadHiding.hideHide(root.parentNode); + } else { + replyHiding.hideHide(root.previousSibling); + } + return true; + }; }, node: function(root) { - if (!root.className) { - if (filter.callbacks.some(function(callback) { - return callback(root); - })) { - return replyHiding.hideHide($('td:not([nowrap])', root)); - } - } else if (root.className === 'op' && !g.REPLY && conf['Filter OPs']) { - if (filter.callbacks.some(function(callback) { - return callback(root); - })) { - return threadHiding.hideHide(root.parentNode); - } + var isOP, key, klass; + klass = root.className; + if (/\binlined\b/.test(klass)) return; + if (!(isOP = klass === 'op')) root = $('td[id]', root); + for (key in filter.filters) { + if (filter.test(root, key, isOP)) return; } }, - test: function(key, value) { - return filter.regexps[key].some(function(regexp) { - return regexp.test(value); - }); + test: function(root, key, isOP) { + var filter, value, _i, _len, _ref; + value = this[key](root, isOP); + if (value === false) return false; + _ref = this.filters[key]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + filter = _ref[_i]; + if (filter(root, value, isOP)) return true; + } + return false; }, - name: function(root) { + name: function(root, isOP) { var name; - name = root.className === 'op' ? $('.postername', root) : $('.commentpostername', root); - return filter.test('name', name.textContent); + name = isOP ? $('.postername', root) : $('.commentpostername', root); + return name.textContent; }, tripcode: function(root) { var trip; - if (trip = $('.postertrip', root)) { - return filter.test('tripcode', trip.textContent); - } + if (trip = $('.postertrip', root)) return trip.textContent; + return false; }, email: function(root) { var mail; - if (mail = $('.linkmail', root)) return filter.test('email', mail.href); + if (!(mail = $('.linkmail', root))) return mail.href; + return false; }, - subject: function(root) { + subject: function(root, isOP) { var sub; - sub = root.className === 'op' ? $('.filetitle', root) : $('.replytitle', root); - return filter.test('subject', sub.textContent); + sub = isOP ? $('.filetitle', root) : $('.replytitle', root); + return sub.textContent; }, comment: function(root) { - return filter.test('comment', ($.el('a', { + return ($.el('a', { innerHTML: $('blockquote', root).innerHTML.replace(/
/g, '\n') - })).textContent); + })).textContent; }, filename: function(root) { var file; - if (file = $('.filesize span', root)) { - return filter.test('filename', file.title); - } + if (file = $('.filesize > span', root)) return file.title; + return false; }, filesize: function(root) { var img; - if (img = $('img[md5]', root)) return filter.test('filesize', img.alt); + if (img = $('img[md5]', root)) return img.alt; + return false; }, md5: function(root) { var img; - if (img = $('img[md5]', root)) { - return filter.test('md5', img.getAttribute('md5')); - } + if (img = $('img[md5]', root)) return img.getAttribute('md5'); + return false; } }; @@ -4032,13 +4053,16 @@ img[md5], img[md5] + img {\ .inlined {\ opacity: .5;\ }\ -.inline td.reply {\ +.inline .reply {\ background-color: rgba(255, 255, 255, 0.15);\ border: 1px solid rgba(128, 128, 128, 0.5);\ }\ .filetitle, .replytitle, .postername, .commentpostername, .postertrip {\ background: none;\ }\ +.filter_highlight {\ + box-shadow: -5px 0 rgba(255,0,0,0.5);\ +}\ .filtered {\ text-decoration: line-through;\ }\ diff --git a/script.coffee b/script.coffee index d2b2d593a..2350c11d2 100644 --- a/script.coffee +++ b/script.coffee @@ -13,9 +13,8 @@ config = 'Check for Updates': [true, 'Check for updated versions of 4chan X'] Filtering: 'Anonymize': [false, 'Make everybody anonymous'] - 'Filter': [false, 'Self-moderation placebo'] - 'Filter OPs': [false, 'Filter OPs along with their threads'] - 'Recursive Filtering': [false, 'Filter replies of filtered posts, recursively'] + 'Filter': [true, 'Self-moderation placebo'] + 'Recursive Filtering': [true, 'Filter replies of filtered posts, recursively'] 'Reply Hiding': [true, 'Hide single replies'] 'Thread Hiding': [true, 'Hide entire threads'] 'Show Stubs': [true, 'Of hidden threads / replies'] @@ -408,58 +407,113 @@ $$ = (selector, root=d.body) -> Array::slice.call root.querySelectorAll selector filter = - regexps: {} - callbacks: [] + filters: {} init: -> for key of config.filter - unless m = conf[key].match /^\/.+\/\w*$/gm - continue - @regexps[key] = [] - for filter in m - f = filter.match /^\/(.+)\/(\w*)$/ - try - @regexps[key].push RegExp f[1], f[2] - catch e - alert e.message - #only execute what's filterable - @callbacks.push @[key] + @filters[key] = [] + for filter in conf[key].split('\n') + continue if filter[0] is '#' - g.callbacks.push @node + unless regexp = filter.match /\/(.+)\/(\w*)/ + continue + + # Don't mix up filter flags with the regular expression. + filter = filter.replace regexp[0], '' + + # Do not add this filter to the list if it's not a global one + # and it's not specifically applicable to the current board. + # Defaults to global. + boards = filter.match(/boards:([^;]+)/)?[1].toLowerCase() or 'global' + if boards isnt 'global' and boards.split(',').indexOf(g.BOARD) is -1 + continue + + try + # Please, don't write silly regular expressions. + regexp = RegExp regexp[1], regexp[2] + catch e + # I warned you, bro. + alert e.message + continue + + # Filter OPs along with their threads, replies only, or both. + # Defaults to replies only. + op = filter.match(/op:(yes|no|only)/)?[1].toLowerCase() or 'no' + + # Highlight the post, or hide it. + # Defaults to post hiding. + hl = /highlight/.test filter + + @filters[key].push @createFilter regexp, op, hl + + # Only execute filter types that contain valid filters. + unless @filters[key].length + delete @filters[key] + + if Object.keys(@filters).length + g.callbacks.push @node + + createFilter: (regexp, op, hl) -> + (root, value, isOP) -> + if isOP and op is 'no' or op is 'only' + return false + unless regexp.test value + return false + if hl + $.addClass root, 'filter_highlight' + else if isOP + threadHiding.hideHide root.parentNode + else + replyHiding.hideHide root.previousSibling + true node: (root) -> - unless root.className - if filter.callbacks.some((callback) -> callback root) - replyHiding.hideHide $ 'td:not([nowrap])', root - else if root.className is 'op' and not g.REPLY and conf['Filter OPs'] - if filter.callbacks.some((callback) -> callback root) - threadHiding.hideHide root.parentNode + klass = root.className + return if /\binlined\b/.test klass + unless isOP = klass is 'op' + root = $ 'td[id]', root + for key of filter.filters + if filter.test root, key, isOP + return - test: (key, value) -> - filter.regexps[key].some (regexp) -> regexp.test value + test: (root, key, isOP) -> + value = @[key] root, isOP + if value is false + # Return if there's nothing to filter (no tripcode for example). + return false - name: (root) -> - name = if root.className is 'op' then $ '.postername', root else $ '.commentpostername', root - filter.test 'name', name.textContent + for filter in @filters[key] + if filter root, value, isOP + return true + false + + name: (root, isOP) -> + name = if isOP then $ '.postername', root else $ '.commentpostername', root + name.textContent tripcode: (root) -> if trip = $ '.postertrip', root - filter.test 'tripcode', trip.textContent + return trip.textContent + false email: (root) -> - if mail = $ '.linkmail', root - filter.test 'email', mail.href - subject: (root) -> - sub = if root.className is 'op' then $ '.filetitle', root else $ '.replytitle', root - filter.test 'subject', sub.textContent + unless mail = $ '.linkmail', root + return mail.href + false + subject: (root, isOP) -> + sub = if isOP then $ '.filetitle', root else $ '.replytitle', root + sub.textContent comment: (root) -> - filter.test 'comment', ($.el 'a', innerHTML: $('blockquote', root).innerHTML.replace /
/g, '\n').textContent + ($.el 'a', innerHTML: $('blockquote', root).innerHTML.replace /
/g, '\n').textContent filename: (root) -> - if file = $ '.filesize span', root - filter.test 'filename', file.title + if file = $ '.filesize > span', root + return file.title + false filesize: (root) -> if img = $ 'img[md5]', root - filter.test 'filesize', img.alt + return img.alt + false md5: (root) -> if img = $ 'img[md5]', root - filter.test 'md5', img.getAttribute('md5') + return img.getAttribute 'md5' + false strikethroughQuotes = init: -> @@ -3303,13 +3357,16 @@ img[md5], img[md5] + img { .inlined { opacity: .5; } -.inline td.reply { +.inline .reply { background-color: rgba(255, 255, 255, 0.15); border: 1px solid rgba(128, 128, 128, 0.5); } .filetitle, .replytitle, .postername, .commentpostername, .postertrip { background: none; } +.filter_highlight { + box-shadow: -5px 0 rgba(255,0,0,0.5); +} .filtered { text-decoration: line-through; } From f6f1f2293c0ed37ddd0a43df8929edfa264ae659 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 02:22:08 +0100 Subject: [PATCH 10/28] Fix op filtering only. --- 4chan_x.user.js | 2 +- script.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index a66c387ca..52edf26ee 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -562,7 +562,7 @@ }, createFilter: function(regexp, op, hl) { return function(root, value, isOP) { - if (isOP && op === 'no' || op === 'only') return false; + if (isOP && op === 'no' || !isOP && op === 'only') return false; if (!regexp.test(value)) return false; if (hl) { $.addClass(root, 'filter_highlight'); diff --git a/script.coffee b/script.coffee index 6cb960952..71d20df85 100644 --- a/script.coffee +++ b/script.coffee @@ -454,7 +454,7 @@ filter = createFilter: (regexp, op, hl) -> (root, value, isOP) -> - if isOP and op is 'no' or op is 'only' + if isOP and op is 'no' or !isOP and op is 'only' return false unless regexp.test value return false From f01ae0a9db6e3a6c793c958ecb0adaa20aa553d2 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 02:30:00 +0100 Subject: [PATCH 11/28] Add image dimensions filtering. --- 4chan_x.user.js | 7 +++++++ script.coffee | 22 ++++++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 52edf26ee..714a8cef6 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -141,6 +141,7 @@ subject: '', comment: '', filename: '', + dimensions: '', filesize: '', md5: '' }, @@ -624,6 +625,11 @@ if (file = $('.filesize > span', root)) return file.title; return false; }, + dimensions: function(root) { + var span; + if (span = $('.filesize', root)) return span.textContent.match(/\d+x\d+/)[0]; + return false; + }, filesize: function(root) { var img; if (img = $('img[md5]', root)) return img.alt; @@ -2111,6 +2117,7 @@

Subject:

\

Comment:

\

Filename:

\ +

Image dimensions:

\

Filesize:

\

Image MD5:

\ \ diff --git a/script.coffee b/script.coffee index 71d20df85..42bcd1161 100644 --- a/script.coffee +++ b/script.coffee @@ -52,14 +52,15 @@ config = 'Indicate Cross-thread Quotes': [true, 'Add \'(Cross-thread)\' to cross-threads quotes'] 'Forward Hiding': [true, 'Hide original posts of inlined backlinks'] filter: - name: '' - tripcode: '' - email: '' - subject: '' - comment: '' - filename: '' - filesize: '' - md5: '' + name: '' + tripcode: '' + email: '' + subject: '' + comment: '' + filename: '' + dimensions: '' + filesize: '' + md5: '' sauces: [ 'http://iqdb.org/?url=$1' 'http://www.google.com/searchbyimage?image_url=$1' @@ -506,6 +507,10 @@ filter = if file = $ '.filesize > span', root return file.title false + dimensions: (root) -> + if span = $ '.filesize', root + return span.textContent.match(/\d+x\d+/)[0] + return false filesize: (root) -> if img = $ 'img[md5]', root return img.alt @@ -1683,6 +1688,7 @@ options =

Subject:

Comment:

Filename:

+

Image dimensions:

Filesize:

Image MD5:

From 84cf869181cc9cfbffb20f71a360a0bd943baace Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 02:32:41 +0100 Subject: [PATCH 12/28] Fix #240. --- 4chan_x.user.js | 2 +- changelog | 1 + script.coffee | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 714a8cef6..2047a403e 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1705,7 +1705,7 @@ }); ta.style.cssText = $.get('qr.size', ''); } - mimeTypes = $('.rules').textContent.match(/: (.+) /)[1].toLowerCase().replace(/\w+/g, function(type) { + mimeTypes = $('.rules').firstChild.textContent.match(/: (.+) /)[1].toLowerCase().replace(/\w+/g, function(type) { switch (type) { case 'jpg': return 'image/jpeg'; diff --git a/changelog b/changelog index 496914583..d4736eca0 100644 --- a/changelog +++ b/changelog @@ -1,6 +1,7 @@ master - Mayhem Fix posting on Safari. + Fix rare case where the QR would not accept images. 2.26.4 - Mayhem diff --git a/script.coffee b/script.coffee index 42bcd1161..e2f0b7d1e 100644 --- a/script.coffee +++ b/script.coffee @@ -1325,7 +1325,7 @@ qr = ta.style.cssText = $.get 'qr.size', '' # Allow only this board's supported files. - mimeTypes = $('.rules').textContent.match(/: (.+) /)[1].toLowerCase().replace /\w+/g, (type) -> + mimeTypes = $('.rules').firstChild.textContent.match(/: (.+) /)[1].toLowerCase().replace /\w+/g, (type) -> switch type when 'jpg' 'image/jpeg' From 1eb1d2f268f2201b388c3695c2e9b2af7c9f512c Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 02:38:14 +0100 Subject: [PATCH 13/28] Filter changelogs --- changelog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog b/changelog index d4736eca0..e9809f967 100644 --- a/changelog +++ b/changelog @@ -1,5 +1,10 @@ master - Mayhem + The Filter now has per filter settings: + - Filter the OP along its thread, replies only, or both. + - Per boards, or global. + - Hightlight, or hide. + New filter group: Image dimensions. Fix posting on Safari. Fix rare case where the QR would not accept images. From 75d8209d7a6deced31d45c0b7f8c7d20f8a8ed87 Mon Sep 17 00:00:00 2001 From: ahodesuka Date: Mon, 20 Feb 2012 19:43:29 -0600 Subject: [PATCH 14/28] Revert return of auto noko. --- changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog b/changelog index 8007556dc..001f66986 100644 --- a/changelog +++ b/changelog @@ -95,7 +95,7 @@ master see https://github.com/MayhemYDG/4chan-x/issues/136 2.24.4 -- ahokadesuka +- ahodesuka Scroll back up when unexpanding images. - e000 Prevent absurd cooldown durations. From 15b7a55fe01b90965f3663202b71d799456ff95e Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 03:00:12 +0100 Subject: [PATCH 15/28] Schedule Main.addStyle before something breaks. Otherwise the options are 'hidden' at the bottom of the page. --- 4chan_x.user.js | 8 ++++---- changelog | 2 +- script.coffee | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 2047a403e..14245391a 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -3576,6 +3576,10 @@ return; } $.ready(options.init); + if (conf['Quick Reply'] && conf['Hide Original Post Form']) { + Main.css += 'form[name=post] { display: none; }'; + } + Main.addStyle(); now = Date.now(); if (conf['Check for Updates'] && $.get('lastUpdate', 0) < now - 6 * HOUR) { $.ready(function() { @@ -3619,10 +3623,6 @@ quoteIndicators.init(); } if (conf['Fix XXX\'d Post Numbers']) unxify.init(); - if (conf['Quick Reply'] && conf['Hide Original Post Form']) { - Main.css += 'form[name=post] { display: none; }'; - } - Main.addStyle(); return $.ready(Main.ready); }, ready: function() { diff --git a/changelog b/changelog index e9809f967..85068b94a 100644 --- a/changelog +++ b/changelog @@ -6,7 +6,7 @@ master - Hightlight, or hide. New filter group: Image dimensions. Fix posting on Safari. - Fix rare case where the QR would not accept images. + Fix rare case where the QR would not accept certain image types. 2.26.4 - Mayhem diff --git a/script.coffee b/script.coffee index e2f0b7d1e..693b01ba7 100644 --- a/script.coffee +++ b/script.coffee @@ -2848,6 +2848,11 @@ Main = $.ready options.init + if conf['Quick Reply'] and conf['Hide Original Post Form'] + Main.css += 'form[name=post] { display: none; }' + + Main.addStyle() + now = Date.now() if conf['Check for Updates'] and $.get('lastUpdate', 0) < now - 6*HOUR $.ready -> $.add d.head, $.el 'script', src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js' @@ -2918,11 +2923,6 @@ Main = if conf['Fix XXX\'d Post Numbers'] unxify.init() - if conf['Quick Reply'] and conf['Hide Original Post Form'] - Main.css += 'form[name=post] { display: none; }' - - Main.addStyle() - $.ready Main.ready ready: -> From a07fb2c9cbed3856b0f4b9246964098e5f19d46e Mon Sep 17 00:00:00 2001 From: ahodesuka Date: Mon, 20 Feb 2012 20:03:26 -0600 Subject: [PATCH 16/28] Auto noko to replies from the main board when not in dump mode. --- 4chan_x.user.js | 169 +++++++++++++++++++++++++------------------ changelog | 4 +- script.coffee | 186 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 224 insertions(+), 135 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index f1bcab6b6..3324b7de9 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -90,9 +90,8 @@ }, Filtering: { 'Anonymize': [false, 'Make everybody anonymous'], - 'Filter': [false, 'Self-moderation placebo'], - 'Filter OPs': [false, 'Filter OPs along with their threads'], - 'Recursive Filtering': [false, 'Filter replies of filtered posts, recursively'], + 'Filter': [true, 'Self-moderation placebo'], + 'Recursive Filtering': [true, 'Filter replies of filtered posts, recursively'], 'Reply Hiding': [true, 'Hide single replies'], 'Thread Hiding': [true, 'Hide entire threads'], 'Show Stubs': [true, 'Of hidden threads / replies'] @@ -100,7 +99,6 @@ Imaging: { 'Image Auto-Gif': [false, 'Animate gif thumbnails'], 'Image Expansion': [true, 'Expand images'], - 'Expand From Current': [true, 'Expand images from current position to thread end'], 'Image Hover': [false, 'Show full image on mouseover'], 'Sauce': [true, 'Add sauce to images'], 'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail'] @@ -120,6 +118,7 @@ 'Cooldown': [true, 'Prevent "flood detected" errors.'], 'Persistent QR': [false, 'The Quick reply won\'t disappear after posting.'], 'Auto Hide QR': [true, 'Automatically hide the quick reply when posting.'], + 'Auto Noko': [true, 'Always redirect to your post unless you are in dump mode'], 'Remember QR size': [false, 'Remember the size of the Quick reply (Firefox only).'], 'Remember Subject': [false, 'Remember the subject field, instead of resetting after posting.'], 'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'], @@ -143,6 +142,7 @@ subject: '', comment: '', filename: '', + dimensions: '', filesize: '', md5: '' }, @@ -169,7 +169,7 @@ expandThread: ['e', 'Expand thread'], watch: ['w', 'Watch thread'], hide: ['x', 'Hide thread'], - expandImage: ['m', 'Expand selected image'], + expandImages: ['m', 'Expand selected image'], expandAllImages: ['M', 'Expand all images'], update: ['u', 'Update now'], unreadCountTo0: ['z', 'Reset unread status'] @@ -533,86 +533,113 @@ }; filter = { - regexps: {}, - callbacks: [], + filters: {}, init: function() { - var f, filter, key, m, _i, _len; + var boards, filter, hl, key, op, regexp, _i, _len, _ref, _ref2, _ref3; for (key in config.filter) { - if (!(m = conf[key].match(/^\/.+\/\w*$/gm))) continue; - this.regexps[key] = []; - for (_i = 0, _len = m.length; _i < _len; _i++) { - filter = m[_i]; - f = filter.match(/^\/(.+)\/(\w*)$/); + this.filters[key] = []; + _ref = conf[key].split('\n'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + filter = _ref[_i]; + if (filter[0] === '#') continue; + if (!(regexp = filter.match(/\/(.+)\/(\w*)/))) continue; + filter = filter.replace(regexp[0], ''); + boards = ((_ref2 = filter.match(/boards:([^;]+)/)) != null ? _ref2[1].toLowerCase() : void 0) || 'global'; + if (boards !== 'global' && boards.split(',').indexOf(g.BOARD) === -1) { + continue; + } try { - this.regexps[key].push(RegExp(f[1], f[2])); + regexp = RegExp(regexp[1], regexp[2]); } catch (e) { alert(e.message); + continue; } + op = ((_ref3 = filter.match(/op:(yes|no|only)/)) != null ? _ref3[1].toLowerCase() : void 0) || 'no'; + hl = /highlight/.test(filter); + this.filters[key].push(this.createFilter(regexp, op, hl)); } - this.callbacks.push(this[key]); + if (!this.filters[key].length) delete this.filters[key]; } - return g.callbacks.push(this.node); + if (Object.keys(this.filters).length) return g.callbacks.push(this.node); + }, + createFilter: function(regexp, op, hl) { + return function(root, value, isOP) { + if (isOP && op === 'no' || !isOP && op === 'only') return false; + if (!regexp.test(value)) return false; + if (hl) { + $.addClass(root, 'filter_highlight'); + } else if (isOP) { + threadHiding.hideHide(root.parentNode); + } else { + replyHiding.hideHide(root.previousSibling); + } + return true; + }; }, node: function(root) { - if (!root.className) { - if (filter.callbacks.some(function(callback) { - return callback(root); - })) { - return replyHiding.hideHide($('td:not([nowrap])', root)); - } - } else if (root.className === 'op' && !g.REPLY && conf['Filter OPs']) { - if (filter.callbacks.some(function(callback) { - return callback(root); - })) { - return threadHiding.hideHide(root.parentNode); - } + var isOP, key, klass; + klass = root.className; + if (/\binlined\b/.test(klass)) return; + if (!(isOP = klass === 'op')) root = $('td[id]', root); + for (key in filter.filters) { + if (filter.test(root, key, isOP)) return; } }, - test: function(key, value) { - return filter.regexps[key].some(function(regexp) { - return regexp.test(value); - }); + test: function(root, key, isOP) { + var filter, value, _i, _len, _ref; + value = this[key](root, isOP); + if (value === false) return false; + _ref = this.filters[key]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + filter = _ref[_i]; + if (filter(root, value, isOP)) return true; + } + return false; }, - name: function(root) { + name: function(root, isOP) { var name; - name = root.className === 'op' ? $('.postername', root) : $('.commentpostername', root); - return filter.test('name', name.textContent); + name = isOP ? $('.postername', root) : $('.commentpostername', root); + return name.textContent; }, tripcode: function(root) { var trip; - if (trip = $('.postertrip', root)) { - return filter.test('tripcode', trip.textContent); - } + if (trip = $('.postertrip', root)) return trip.textContent; + return false; }, email: function(root) { var mail; - if (mail = $('.linkmail', root)) return filter.test('email', mail.href); + if (!(mail = $('.linkmail', root))) return mail.href; + return false; }, - subject: function(root) { + subject: function(root, isOP) { var sub; - sub = root.className === 'op' ? $('.filetitle', root) : $('.replytitle', root); - return filter.test('subject', sub.textContent); + sub = isOP ? $('.filetitle', root) : $('.replytitle', root); + return sub.textContent; }, comment: function(root) { - return filter.test('comment', ($.el('a', { + return ($.el('a', { innerHTML: $('blockquote', root).innerHTML.replace(/
/g, '\n') - })).textContent); + })).textContent; }, filename: function(root) { var file; - if (file = $('.filesize span', root)) { - return filter.test('filename', file.title); - } + if (file = $('.filesize > span', root)) return file.title; + return false; + }, + dimensions: function(root) { + var span; + if (span = $('.filesize', root)) return span.textContent.match(/\d+x\d+/)[0]; + return false; }, filesize: function(root) { var img; - if (img = $('img[md5]', root)) return filter.test('filesize', img.alt); + if (img = $('img[md5]', root)) return img.alt; + return false; }, md5: function(root) { var img; - if (img = $('img[md5]', root)) { - return filter.test('md5', img.getAttribute('md5')); - } + if (img = $('img[md5]', root)) return img.getAttribute('md5'); + return false; } }; @@ -933,7 +960,7 @@ case conf.expandThread: expandThread.toggle(thread); break; - case conf.expandImage: + case conf.expandImages: keybinds.img(thread); break; case conf.nextThread: @@ -1224,7 +1251,7 @@ $.before(form, link); } g.callbacks.push(this.node); - if (engine === 'webkit') { + if (/chrome/i.test(navigator.userAgent)) { qr.status({ ready: true }); @@ -1679,7 +1706,7 @@ }); ta.style.cssText = $.get('qr.size', ''); } - mimeTypes = $('.rules').textContent.match(/: (.+) /)[1].toLowerCase().replace(/\w+/g, function(type) { + mimeTypes = $('.rules').firstChild.textContent.match(/: (.+) /)[1].toLowerCase().replace(/\w+/g, function(type) { switch (type) { case 'jpg': return 'image/jpeg'; @@ -1833,7 +1860,7 @@ reader.readAsBinaryString(reply.file); return; } - if (engine === 'webkit') { + if (/chrome/i.test(navigator.userAgent)) { qr.message.post(post); return; } @@ -1888,6 +1915,9 @@ } else { qr.cooldown.auto = qr.replies.length > 1; qr.cooldown.set(/sage/i.test(reply.email) ? 60 : 30); + if (conf['Auto Noko'] && !g.REPLY && !qr.cooldown.auto) { + location.href = "http://boards.4chan.org/" + g.BOARD + "/res/" + thread + "#" + postNumber; + } } if (conf['Persistent QR'] || qr.cooldown.auto) { reply.rm(); @@ -1900,7 +1930,7 @@ message: { send: function(data) { var host, window; - if (engine === 'webkit') { + if (/chrome/i.test(navigator.userAgent)) { qr.message.receive(data); return; } @@ -2091,6 +2121,7 @@

Subject:

\

Comment:

\

Filename:

\ +

Image dimensions:

\

Filesize:

\

Image MD5:

\ \ @@ -3388,25 +3419,18 @@ return imgExpand.toggle(this); }, all: function() { - var i, thumb, thumbs, _i, _j, _len, _len2, _len3, _ref; + var thumb, _i, _j, _len, _len2, _ref, _ref2; imgExpand.on = this.checked; if (imgExpand.on) { - thumbs = $$('img[md5]'); - if (conf['Expand From Current']) { - for (i = 0, _len = thumbs.length; i < _len; i++) { - thumb = thumbs[i]; - if (thumb.getBoundingClientRect().top > 0) break; - } - thumbs = thumbs.slice(i); - } - for (_i = 0, _len2 = thumbs.length; _i < _len2; _i++) { - thumb = thumbs[_i]; + _ref = $$('img[md5]'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + thumb = _ref[_i]; imgExpand.expand(thumb); } } else { - _ref = $$('img[md5][hidden]'); - for (_j = 0, _len3 = _ref.length; _j < _len3; _j++) { - thumb = _ref[_j]; + _ref2 = $$('img[md5][hidden]'); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + thumb = _ref2[_j]; imgExpand.contract(thumb); } } @@ -4040,13 +4064,16 @@ img[md5], img[md5] + img {\ .inlined {\ opacity: .5;\ }\ -.inline td.reply {\ +.inline .reply {\ background-color: rgba(255, 255, 255, 0.15);\ border: 1px solid rgba(128, 128, 128, 0.5);\ }\ .filetitle, .replytitle, .postername, .commentpostername, .postertrip {\ background: none;\ }\ +.filter_highlight {\ + box-shadow: -5px 0 rgba(255,0,0,0.5);\ +}\ .filtered {\ text-decoration: line-through;\ }\ diff --git a/changelog b/changelog index 001f66986..f3258e431 100644 --- a/changelog +++ b/changelog @@ -1,6 +1,6 @@ master -- aeosynth / ahodesuka - expand images from current position +- ahodesuka + Add auto noko option for replies from the main board and not dumping. 2.26.4 - Mayhem diff --git a/script.coffee b/script.coffee index fab4742ab..f4b95367f 100644 --- a/script.coffee +++ b/script.coffee @@ -13,16 +13,14 @@ config = 'Check for Updates': [true, 'Check for updated versions of 4chan X'] Filtering: 'Anonymize': [false, 'Make everybody anonymous'] - 'Filter': [false, 'Self-moderation placebo'] - 'Filter OPs': [false, 'Filter OPs along with their threads'] - 'Recursive Filtering': [false, 'Filter replies of filtered posts, recursively'] + 'Filter': [true, 'Self-moderation placebo'] + 'Recursive Filtering': [true, 'Filter replies of filtered posts, recursively'] 'Reply Hiding': [true, 'Hide single replies'] 'Thread Hiding': [true, 'Hide entire threads'] 'Show Stubs': [true, 'Of hidden threads / replies'] Imaging: 'Image Auto-Gif': [false, 'Animate gif thumbnails'] 'Image Expansion': [true, 'Expand images'] - 'Expand From Current': [true, 'Expand images from current position to thread end'] 'Image Hover': [false, 'Show full image on mouseover'] 'Sauce': [true, 'Add sauce to images'] 'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail'] @@ -40,6 +38,7 @@ config = 'Cooldown': [true, 'Prevent "flood detected" errors.'] 'Persistent QR': [false, 'The Quick reply won\'t disappear after posting.'] 'Auto Hide QR': [true, 'Automatically hide the quick reply when posting.'] + 'Auto Noko': [true, 'Always redirect to your post unless you are in dump mode'] 'Remember QR size': [false, 'Remember the size of the Quick reply (Firefox only).'] 'Remember Subject': [false, 'Remember the subject field, instead of resetting after posting.'] 'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'] @@ -54,14 +53,15 @@ config = 'Indicate Cross-thread Quotes': [true, 'Add \'(Cross-thread)\' to cross-threads quotes'] 'Forward Hiding': [true, 'Hide original posts of inlined backlinks'] filter: - name: '' - tripcode: '' - email: '' - subject: '' - comment: '' - filename: '' - filesize: '' - md5: '' + name: '' + tripcode: '' + email: '' + subject: '' + comment: '' + filename: '' + dimensions: '' + filesize: '' + md5: '' sauces: [ 'http://iqdb.org/?url=$1' 'http://www.google.com/searchbyimage?image_url=$1' @@ -98,7 +98,7 @@ config = expandThread: ['e', 'Expand thread'] watch: ['w', 'Watch thread'] hide: ['x', 'Hide thread'] - expandImage: ['m', 'Expand selected image'] + expandImages: ['m', 'Expand selected image'] expandAllImages: ['M', 'Expand all images'] update: ['u', 'Update now'] unreadCountTo0: ['z', 'Reset unread status'] @@ -409,58 +409,117 @@ $$ = (selector, root=d.body) -> Array::slice.call root.querySelectorAll selector filter = - regexps: {} - callbacks: [] + filters: {} init: -> for key of config.filter - unless m = conf[key].match /^\/.+\/\w*$/gm - continue - @regexps[key] = [] - for filter in m - f = filter.match /^\/(.+)\/(\w*)$/ - try - @regexps[key].push RegExp f[1], f[2] - catch e - alert e.message - #only execute what's filterable - @callbacks.push @[key] + @filters[key] = [] + for filter in conf[key].split('\n') + continue if filter[0] is '#' - g.callbacks.push @node + unless regexp = filter.match /\/(.+)\/(\w*)/ + continue + + # Don't mix up filter flags with the regular expression. + filter = filter.replace regexp[0], '' + + # Do not add this filter to the list if it's not a global one + # and it's not specifically applicable to the current board. + # Defaults to global. + boards = filter.match(/boards:([^;]+)/)?[1].toLowerCase() or 'global' + if boards isnt 'global' and boards.split(',').indexOf(g.BOARD) is -1 + continue + + try + # Please, don't write silly regular expressions. + regexp = RegExp regexp[1], regexp[2] + catch e + # I warned you, bro. + alert e.message + continue + + # Filter OPs along with their threads, replies only, or both. + # Defaults to replies only. + op = filter.match(/op:(yes|no|only)/)?[1].toLowerCase() or 'no' + + # Highlight the post, or hide it. + # Defaults to post hiding. + hl = /highlight/.test filter + + @filters[key].push @createFilter regexp, op, hl + + # Only execute filter types that contain valid filters. + unless @filters[key].length + delete @filters[key] + + if Object.keys(@filters).length + g.callbacks.push @node + + createFilter: (regexp, op, hl) -> + (root, value, isOP) -> + if isOP and op is 'no' or !isOP and op is 'only' + return false + unless regexp.test value + return false + if hl + $.addClass root, 'filter_highlight' + else if isOP + threadHiding.hideHide root.parentNode + else + replyHiding.hideHide root.previousSibling + true node: (root) -> - unless root.className - if filter.callbacks.some((callback) -> callback root) - replyHiding.hideHide $ 'td:not([nowrap])', root - else if root.className is 'op' and not g.REPLY and conf['Filter OPs'] - if filter.callbacks.some((callback) -> callback root) - threadHiding.hideHide root.parentNode + klass = root.className + return if /\binlined\b/.test klass + unless isOP = klass is 'op' + root = $ 'td[id]', root + for key of filter.filters + if filter.test root, key, isOP + return - test: (key, value) -> - filter.regexps[key].some (regexp) -> regexp.test value + test: (root, key, isOP) -> + value = @[key] root, isOP + if value is false + # Return if there's nothing to filter (no tripcode for example). + return false - name: (root) -> - name = if root.className is 'op' then $ '.postername', root else $ '.commentpostername', root - filter.test 'name', name.textContent + for filter in @filters[key] + if filter root, value, isOP + return true + false + + name: (root, isOP) -> + name = if isOP then $ '.postername', root else $ '.commentpostername', root + name.textContent tripcode: (root) -> if trip = $ '.postertrip', root - filter.test 'tripcode', trip.textContent + return trip.textContent + false email: (root) -> - if mail = $ '.linkmail', root - filter.test 'email', mail.href - subject: (root) -> - sub = if root.className is 'op' then $ '.filetitle', root else $ '.replytitle', root - filter.test 'subject', sub.textContent + unless mail = $ '.linkmail', root + return mail.href + false + subject: (root, isOP) -> + sub = if isOP then $ '.filetitle', root else $ '.replytitle', root + sub.textContent comment: (root) -> - filter.test 'comment', ($.el 'a', innerHTML: $('blockquote', root).innerHTML.replace /
/g, '\n').textContent + ($.el 'a', innerHTML: $('blockquote', root).innerHTML.replace /
/g, '\n').textContent filename: (root) -> - if file = $ '.filesize span', root - filter.test 'filename', file.title + if file = $ '.filesize > span', root + return file.title + false + dimensions: (root) -> + if span = $ '.filesize', root + return span.textContent.match(/\d+x\d+/)[0] + return false filesize: (root) -> if img = $ 'img[md5]', root - filter.test 'filesize', img.alt + return img.alt + false md5: (root) -> if img = $ 'img[md5]', root - filter.test 'md5', img.getAttribute('md5') + return img.getAttribute 'md5' + false strikethroughQuotes = init: -> @@ -699,7 +758,7 @@ keybinds = keybinds.open thread when conf.expandThread expandThread.toggle thread - when conf.expandImage + when conf.expandImages keybinds.img thread when conf.nextThread return if g.REPLY @@ -901,7 +960,8 @@ qr = $.before form, link g.callbacks.push @node - if engine is 'webkit' + # CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox. + if /chrome/i.test navigator.userAgent qr.status ready: true else iframe = $.el 'iframe', @@ -1266,7 +1326,7 @@ qr = ta.style.cssText = $.get 'qr.size', '' # Allow only this board's supported files. - mimeTypes = $('.rules').textContent.match(/: (.+) /)[1].toLowerCase().replace /\w+/g, (type) -> + mimeTypes = $('.rules').firstChild.textContent.match(/: (.+) /)[1].toLowerCase().replace /\w+/g, (type) -> switch type when 'jpg' 'image/jpeg' @@ -1406,7 +1466,8 @@ qr = reader.readAsBinaryString reply.file return - if engine is 'webkit' + # CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox. + if /chrome/i.test navigator.userAgent qr.message.post post return qr.message.send post @@ -1457,6 +1518,8 @@ qr = # Enable auto-posting if we have stuff to post, disable it otherwise. qr.cooldown.auto = qr.replies.length > 1 qr.cooldown.set if /sage/i.test reply.email then 60 else 30 + # auto-noko + location.href = "http://boards.4chan.org/#{g.BOARD}/res/#{thread}##{postNumber}" if conf['Auto Noko'] && !g.REPLY && !qr.cooldown.auto if conf['Persistent QR'] or qr.cooldown.auto reply.rm() @@ -1468,7 +1531,8 @@ qr = message: send: (data) -> - if engine is 'webkit' + # CORS is ignored for content script on Chrome, but not Safari/Oprah/Firefox. + if /chrome/i.test navigator.userAgent qr.message.receive data return data.qr = true @@ -1627,6 +1691,7 @@ options =

Subject:

Comment:

Filename:

+

Image dimensions:

Filesize:

Image MD5:

@@ -2664,13 +2729,7 @@ imgExpand = all: -> imgExpand.on = @checked if imgExpand.on #expand - thumbs = $$ 'img[md5]' - if conf['Expand From Current'] - for thumb, i in thumbs - if thumb.getBoundingClientRect().top > 0 - break - thumbs = thumbs[i...] - for thumb in thumbs + for thumb in $$ 'img[md5]' imgExpand.expand thumb else #contract for thumb in $$ 'img[md5][hidden]' @@ -3310,13 +3369,16 @@ img[md5], img[md5] + img { .inlined { opacity: .5; } -.inline td.reply { +.inline .reply { background-color: rgba(255, 255, 255, 0.15); border: 1px solid rgba(128, 128, 128, 0.5); } .filetitle, .replytitle, .postername, .commentpostername, .postertrip { background: none; } +.filter_highlight { + box-shadow: -5px 0 rgba(255,0,0,0.5); +} .filtered { text-decoration: line-through; } From a9df947517eb2cbb82cb9fc8b524be24ca726588 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 03:13:50 +0100 Subject: [PATCH 17/28] Typo --- changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog b/changelog index 85068b94a..3573ee786 100644 --- a/changelog +++ b/changelog @@ -3,7 +3,7 @@ master The Filter now has per filter settings: - Filter the OP along its thread, replies only, or both. - Per boards, or global. - - Hightlight, or hide. + - Highlight, or hide. New filter group: Image dimensions. Fix posting on Safari. Fix rare case where the QR would not accept certain image types. From e47a3ccf58e0659b9e059161ee6591dca73ccd08 Mon Sep 17 00:00:00 2001 From: ahodesuka Date: Mon, 20 Feb 2012 20:14:59 -0600 Subject: [PATCH 18/28] Clean up changelog. --- changelog | 3 --- 1 file changed, 3 deletions(-) diff --git a/changelog b/changelog index 8b252f753..7b303228c 100644 --- a/changelog +++ b/changelog @@ -1,8 +1,6 @@ master -<<<<<<< HEAD - ahodesuka Add auto noko option for replies from the main board and not dumping. -======= - Mayhem The Filter now has per filter settings: - Filter the OP along its thread, replies only, or both. @@ -11,7 +9,6 @@ master New filter group: Image dimensions. Fix posting on Safari. Fix rare case where the QR would not accept certain image types. ->>>>>>> 15b7a55fe01b90965f3663202b71d799456ff95e 2.26.4 - Mayhem From f474a0c3c8eeedb62a444f4339b83602dc82e06d Mon Sep 17 00:00:00 2001 From: ahodesuka Date: Mon, 20 Feb 2012 20:21:37 -0600 Subject: [PATCH 19/28] Update and build. --- script.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/script.coffee b/script.coffee index f4b95367f..17c305030 100644 --- a/script.coffee +++ b/script.coffee @@ -2851,6 +2851,11 @@ Main = $.ready options.init + if conf['Quick Reply'] and conf['Hide Original Post Form'] + Main.css += 'form[name=post] { display: none; }' + + Main.addStyle() + now = Date.now() if conf['Check for Updates'] and $.get('lastUpdate', 0) < now - 6*HOUR $.ready -> $.add d.head, $.el 'script', src: 'https://raw.github.com/mayhemydg/4chan-x/master/latest.js' @@ -2921,11 +2926,6 @@ Main = if conf['Fix XXX\'d Post Numbers'] unxify.init() - if conf['Quick Reply'] and conf['Hide Original Post Form'] - Main.css += 'form[name=post] { display: none; }' - - Main.addStyle() - $.ready Main.ready ready: -> From 31eea0a7bdfc93e76c9734fd61ef1dd742cba51f Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 04:30:39 +0100 Subject: [PATCH 20/28] Don't hide OP/Thread when in a thread. --- 4chan_x.user.js | 2 +- script.coffee | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 14245391a..071de23c1 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -568,7 +568,7 @@ if (hl) { $.addClass(root, 'filter_highlight'); } else if (isOP) { - threadHiding.hideHide(root.parentNode); + if (!g.REPLY) threadHiding.hideHide(root.parentNode); } else { replyHiding.hideHide(root.previousSibling); } diff --git a/script.coffee b/script.coffee index 693b01ba7..68d23c7fa 100644 --- a/script.coffee +++ b/script.coffee @@ -462,7 +462,8 @@ filter = if hl $.addClass root, 'filter_highlight' else if isOP - threadHiding.hideHide root.parentNode + unless g.REPLY + threadHiding.hideHide root.parentNode else replyHiding.hideHide root.previousSibling true From 70cee9ca8b4a1109cf965d34df3f0dc28a8557b2 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 04:41:40 +0100 Subject: [PATCH 21/28] You can specify a highlight class. Defaults to filter_highlight. --- 4chan_x.user.js | 8 +++++--- script.coffee | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 071de23c1..319323e16 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -534,7 +534,7 @@ filter = { filters: {}, init: function() { - var boards, filter, hl, key, op, regexp, _i, _len, _ref, _ref2, _ref3; + var boards, filter, hl, key, op, regexp, _i, _len, _ref, _ref2, _ref3, _ref4; for (key in config.filter) { this.filters[key] = []; _ref = conf[key].split('\n'); @@ -554,7 +554,9 @@ continue; } op = ((_ref3 = filter.match(/op:(yes|no|only)/)) != null ? _ref3[1].toLowerCase() : void 0) || 'no'; - hl = /highlight/.test(filter); + if (hl = /highlight/.test(filter)) { + hl = ((_ref4 = filter.match(/highlight:(\w+)/)) != null ? _ref4[1].toLowerCase() : void 0) || 'filter_highlight'; + } this.filters[key].push(this.createFilter(regexp, op, hl)); } if (!this.filters[key].length) delete this.filters[key]; @@ -566,7 +568,7 @@ if (isOP && op === 'no' || !isOP && op === 'only') return false; if (!regexp.test(value)) return false; if (hl) { - $.addClass(root, 'filter_highlight'); + $.addClass(root, hl); } else if (isOP) { if (!g.REPLY) threadHiding.hideHide(root.parentNode); } else { diff --git a/script.coffee b/script.coffee index 68d23c7fa..c1aa05e8f 100644 --- a/script.coffee +++ b/script.coffee @@ -441,8 +441,10 @@ filter = op = filter.match(/op:(yes|no|only)/)?[1].toLowerCase() or 'no' # Highlight the post, or hide it. + # If not specified, the highlight class will be filter_highlight. # Defaults to post hiding. - hl = /highlight/.test filter + if hl = /highlight/.test filter + hl = filter.match(/highlight:(\w+)/)?[1].toLowerCase() or 'filter_highlight' @filters[key].push @createFilter regexp, op, hl @@ -460,7 +462,7 @@ filter = unless regexp.test value return false if hl - $.addClass root, 'filter_highlight' + $.addClass root, hl else if isOP unless g.REPLY threadHiding.hideHide root.parentNode From 0297ac9c955b5e87e3c9297fdce7c2a757017041 Mon Sep 17 00:00:00 2001 From: ahodesuka Date: Mon, 20 Feb 2012 21:57:51 -0600 Subject: [PATCH 22/28] Change auto noko to open reply in new tab. --- 4chan_x.user.js | 9 +++++---- changelog | 2 +- script.coffee | 7 ++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 50b08f21e..c228aa4ae 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -118,7 +118,7 @@ 'Cooldown': [true, 'Prevent "flood detected" errors.'], 'Persistent QR': [false, 'The Quick reply won\'t disappear after posting.'], 'Auto Hide QR': [true, 'Automatically hide the quick reply when posting.'], - 'Auto Noko': [true, 'Always redirect to your post unless you are in dump mode'], + 'Open Reply in New Tab': [false, 'Open replies in a new tab that are made from the main board.'], 'Remember QR size': [false, 'Remember the size of the Quick reply (Firefox only).'], 'Remember Subject': [false, 'Remember the subject field, instead of resetting after posting.'], 'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'], @@ -1867,7 +1867,7 @@ return qr.message.send(post); }, response: function(html) { - var b, doc, err, node, persona, postNumber, reply, thread, _, _ref; + var b, doc, err, node, open, persona, postNumber, reply, thread, _, _ref; doc = $.el('a', { innerHTML: html }); @@ -1915,8 +1915,9 @@ } else { qr.cooldown.auto = qr.replies.length > 1; qr.cooldown.set(/sage/i.test(reply.email) ? 60 : 30); - if (conf['Auto Noko'] && !g.REPLY && !qr.cooldown.auto) { - location.href = "http://boards.4chan.org/" + g.BOARD + "/res/" + thread + "#" + postNumber; + if (conf['Open Reply in New Tab'] && !g.REPLY && !qr.cooldown.auto) { + open = GM_openInTab || window.open; + open("http://boards.4chan.org/" + g.BOARD + "/res/" + thread + "#" + postNumber, "_blank"); } } if (conf['Persistent QR'] || qr.cooldown.auto) { diff --git a/changelog b/changelog index 95e73aab9..199f2d762 100644 --- a/changelog +++ b/changelog @@ -1,6 +1,6 @@ master - ahodesuka - Add auto noko option for replies from the main board and not dumping. + Add Open Reply in New Tab option for replies made from the main board (not dumping). - Mayhem The Filter now has per filter settings: - Filter the OP along its thread, replies only, or both. diff --git a/script.coffee b/script.coffee index 17c305030..c6816e961 100644 --- a/script.coffee +++ b/script.coffee @@ -38,7 +38,7 @@ config = 'Cooldown': [true, 'Prevent "flood detected" errors.'] 'Persistent QR': [false, 'The Quick reply won\'t disappear after posting.'] 'Auto Hide QR': [true, 'Automatically hide the quick reply when posting.'] - 'Auto Noko': [true, 'Always redirect to your post unless you are in dump mode'] + 'Open Reply in New Tab': [false, 'Open replies in a new tab that are made from the main board.'] 'Remember QR size': [false, 'Remember the size of the Quick reply (Firefox only).'] 'Remember Subject': [false, 'Remember the subject field, instead of resetting after posting.'] 'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'] @@ -1518,8 +1518,9 @@ qr = # Enable auto-posting if we have stuff to post, disable it otherwise. qr.cooldown.auto = qr.replies.length > 1 qr.cooldown.set if /sage/i.test reply.email then 60 else 30 - # auto-noko - location.href = "http://boards.4chan.org/#{g.BOARD}/res/#{thread}##{postNumber}" if conf['Auto Noko'] && !g.REPLY && !qr.cooldown.auto + if conf['Open Reply in New Tab'] && !g.REPLY && !qr.cooldown.auto + open = GM_openInTab or window.open + open "http://boards.4chan.org/#{g.BOARD}/res/#{thread}##{postNumber}", "_blank" if conf['Persistent QR'] or qr.cooldown.auto reply.rm() From e7d1b1b5a81fc126f0ab1fdb2b56c4574a8d6fe4 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 05:39:49 +0100 Subject: [PATCH 23/28] Add filter examples. Add filter settings manual. --- 4chan_x.user.js | 26 ++++++++++++++++---------- script.coffee | 48 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 319323e16..a7a6b51bc 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -135,15 +135,15 @@ } }, filter: { - name: '', - tripcode: '', - email: '', - subject: '', - comment: '', - filename: '', - dimensions: '', - filesize: '', - md5: '' + name: [''].join('\n'), + tripcode: [''].join('\n'), + email: ['# Filter any e-mails that are not `sage` on /a/ and /jp/:', '#/^(?!sage$)/;boards:a,jp'].join('\n'), + subject: ['# Filter Generals on /v/:', '#/general/i;boards:v;op:only'].join('\n'), + comment: ['# Filter Stallman copypasta on /g/:', '#/what you\'re refer+ing to as linux/i;boards:g'].join('\n'), + filename: [''].join('\n'), + dimensions: ['# Highlight potential wallpapers:', '#/1920x1080/;op:yes;highlight;boards:w,wg'].join('\n'), + filesize: [''].join('\n'), + md5: [''].join('\n') }, sauces: ['http://iqdb.org/?url=$1', 'http://www.google.com/searchbyimage?image_url=$1', '#http://tineye.com/search?url=$1', '#http://saucenao.com/search.php?db=999&url=$1', '#http://3d.iqdb.org/?url=$1', '#http://regex.info/exif.cgi?imgurl=$2', '# uploaders:', '#http://imgur.com/upload?url=$2', '#http://omploader.org/upload?url1=$2', '# "View Same" in archives:', '#http://archive.foolz.us/a/image/$3/', '#http://archive.installgentoo.net/g/image/$3'].join('\n'), time: '%m/%d/%y(%a)%H:%M', @@ -2100,7 +2100,7 @@ \
\
Sauce is disabled.
\ -
Lines starting with a # will be ignored.
\ + Lines starting with a # will be ignored.\
    These variables will be replaced by the corresponding url:\
  • $1: Thumbnail.
  • \
  • $2: Full image.
  • \ @@ -2112,7 +2112,13 @@
    \
    Filter is disabled.
    \ Use regular expressions, one per line.
    \ + Lines starting with a # will be ignored.
    \ For example, /weeaboo/i will filter posts containing `weeaboo` case-insensitive.\ +
      You can use these settings with each regular expression, separate them with semi-colons:\ +
    • Per boards, separate them with commas. It is global if not specified.
      For example: boards:a,jp;.
    • \ +
    • Filter OP along with their threads only (`only`), replies only (`no`, this is default), or both (`yes`).
      For example: op:only;, op:no; or op:yes;.
    • \ +
    • Highlight instead of hiding. You can specify a class name to use with a userstyle.
      For example: highlight; or hightlight:wallpaper;.
    • \ +
    \

    Name:

    \

    Tripcode:

    \

    E-mail:

    \ diff --git a/script.coffee b/script.coffee index c1aa05e8f..4d72667b9 100644 --- a/script.coffee +++ b/script.coffee @@ -52,15 +52,37 @@ config = 'Indicate Cross-thread Quotes': [true, 'Add \'(Cross-thread)\' to cross-threads quotes'] 'Forward Hiding': [true, 'Hide original posts of inlined backlinks'] filter: - name: '' - tripcode: '' - email: '' - subject: '' - comment: '' - filename: '' - dimensions: '' - filesize: '' - md5: '' + name: [ + '' + ].join '\n' + tripcode: [ + '' + ].join '\n' + email: [ + '# Filter any e-mails that are not `sage` on /a/ and /jp/:' + '#/^(?!sage$)/;boards:a,jp' + ].join '\n' + subject: [ + '# Filter Generals on /v/:' + '#/general/i;boards:v;op:only' + ].join '\n' + comment: [ + '# Filter Stallman copypasta on /g/:' + '#/what you\'re refer+ing to as linux/i;boards:g' + ].join '\n' + filename: [ + '' + ].join '\n' + dimensions: [ + '# Highlight potential wallpapers:' + '#/1920x1080/;op:yes;highlight;boards:w,wg' + ].join '\n' + filesize: [ + '' + ].join '\n' + md5: [ + '' + ].join '\n' sauces: [ 'http://iqdb.org/?url=$1' 'http://www.google.com/searchbyimage?image_url=$1' @@ -1672,7 +1694,7 @@ options =
    Sauce is disabled.
    -
    Lines starting with a # will be ignored.
    + Lines starting with a # will be ignored.
      These variables will be replaced by the corresponding url:
    • $1: Thumbnail.
    • $2: Full image.
    • @@ -1684,7 +1706,13 @@ options =
      Filter is disabled.
      Use regular expressions, one per line.
      + Lines starting with a # will be ignored.
      For example, /weeaboo/i will filter posts containing `weeaboo` case-insensitive. +
        You can use these settings with each regular expression, separate them with semicolons: +
      • Per boards, separate them with commas. It is global if not specified.
        For example: boards:a,jp;.
      • +
      • Filter OPs along with their threads only (`only`), replies only (`no`, this is default), or both (`yes`).
        For example: op:only;, op:no; or op:yes;.
      • +
      • Highlight instead of hiding. You can specify a class name to use with a userstyle.
        For example: highlight; or hightlight:wallpaper;.
      • +

      Name:

      Tripcode:

      E-mail:

      From b0d8e849263d722cd0864414275050b63ea2e2b5 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 06:01:43 +0100 Subject: [PATCH 24/28] Put highlighted OPs on top of board pages. --- 4chan_x.user.js | 13 ++++++++++--- changelog | 4 ++-- script.coffee | 10 ++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index a7a6b51bc..cb9926cc9 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -565,10 +565,17 @@ }, createFilter: function(regexp, op, hl) { return function(root, value, isOP) { + var firstThread, thisThread; if (isOP && op === 'no' || !isOP && op === 'only') return false; if (!regexp.test(value)) return false; if (hl) { $.addClass(root, hl); + if (isOP && !g.REPLY) { + thisThread = root.parentNode; + if (firstThread = $('div[class=op]')) { + $.before(firstThread.parentNode, [thisThread, thisThread.nextElementSibling]); + } + } } else if (isOP) { if (!g.REPLY) threadHiding.hideHide(root.parentNode); } else { @@ -2114,10 +2121,10 @@ Use regular expressions, one per line.
      \ Lines starting with a # will be ignored.
      \ For example, /weeaboo/i will filter posts containing `weeaboo` case-insensitive.\ -
        You can use these settings with each regular expression, separate them with semi-colons:\ +
          You can use these settings with each regular expression, separate them with semicolons:\
        • Per boards, separate them with commas. It is global if not specified.
          For example: boards:a,jp;.
        • \ -
        • Filter OP along with their threads only (`only`), replies only (`no`, this is default), or both (`yes`).
          For example: op:only;, op:no; or op:yes;.
        • \ -
        • Highlight instead of hiding. You can specify a class name to use with a userstyle.
          For example: highlight; or hightlight:wallpaper;.
        • \ +
        • Filter OPs only along with their threads (`only`), replies only (`no`, this is default), or both (`yes`).
          For example: op:only;, op:no; or op:yes;.
        • \ +
        • Highlight instead of hiding. Highlighted OPs will have their threads put on top of board pages. You can specify a class name to use with a userstyle.
          For example: highlight; or hightlight:wallpaper;.
        • \
        \

        Name:

        \

        Tripcode:

        \ diff --git a/changelog b/changelog index 3573ee786..b70c27a03 100644 --- a/changelog +++ b/changelog @@ -1,9 +1,9 @@ master - Mayhem The Filter now has per filter settings: - - Filter the OP along its thread, replies only, or both. + - Filter the OP only along its thread, replies only, or both. - Per boards, or global. - - Highlight, or hide. + - Highlight, or hide. Highlighted OPs will have their threads put on top of the board page. New filter group: Image dimensions. Fix posting on Safari. Fix rare case where the QR would not accept certain image types. diff --git a/script.coffee b/script.coffee index 4d72667b9..d9b59f856 100644 --- a/script.coffee +++ b/script.coffee @@ -485,6 +485,12 @@ filter = return false if hl $.addClass root, hl + if isOP and not g.REPLY + # Put the highlighted OPs' threads on top of the board pages... + thisThread = root.parentNode + # ...before the first non highlighted thread. + if firstThread = $ 'div[class=op]' + $.before firstThread.parentNode, [thisThread, thisThread.nextElementSibling] else if isOP unless g.REPLY threadHiding.hideHide root.parentNode @@ -1710,8 +1716,8 @@ options = For example, /weeaboo/i will filter posts containing `weeaboo` case-insensitive.
          You can use these settings with each regular expression, separate them with semicolons:
        • Per boards, separate them with commas. It is global if not specified.
          For example: boards:a,jp;.
        • -
        • Filter OPs along with their threads only (`only`), replies only (`no`, this is default), or both (`yes`).
          For example: op:only;, op:no; or op:yes;.
        • -
        • Highlight instead of hiding. You can specify a class name to use with a userstyle.
          For example: highlight; or hightlight:wallpaper;.
        • +
        • Filter OPs only along with their threads (`only`), replies only (`no`, this is default), or both (`yes`).
          For example: op:only;, op:no; or op:yes;.
        • +
        • Highlight instead of hiding. Highlighted OPs will have their threads put on top of board pages. You can specify a class name to use with a userstyle.
          For example: highlight; or hightlight:wallpaper;.

        Name:

        Tripcode:

        From f02688bd0aa5018565292b3c892af73704fee35c Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 15:10:35 +0100 Subject: [PATCH 25/28] Let the filter add more than one highlight class. --- 4chan_x.user.js | 1 + script.coffee | 2 ++ 2 files changed, 3 insertions(+) diff --git a/4chan_x.user.js b/4chan_x.user.js index cb9926cc9..47a47776f 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -576,6 +576,7 @@ $.before(firstThread.parentNode, [thisThread, thisThread.nextElementSibling]); } } + return false; } else if (isOP) { if (!g.REPLY) threadHiding.hideHide(root.parentNode); } else { diff --git a/script.coffee b/script.coffee index d9b59f856..ef0b5c4c6 100644 --- a/script.coffee +++ b/script.coffee @@ -491,6 +491,8 @@ filter = # ...before the first non highlighted thread. if firstThread = $ 'div[class=op]' $.before firstThread.parentNode, [thisThread, thisThread.nextElementSibling] + # Continue the filter lookup to add more classes or hide it. + return false else if isOP unless g.REPLY threadHiding.hideHide root.parentNode From ddee297a36f68ab4e72d6e7f2890da2fe847334f Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 15:36:09 +0100 Subject: [PATCH 26/28] Add top option for highlighting filters. --- 4chan_x.user.js | 20 ++++++++++++-------- script.coffee | 21 +++++++++++++-------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 47a47776f..b2b765be3 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -141,7 +141,7 @@ subject: ['# Filter Generals on /v/:', '#/general/i;boards:v;op:only'].join('\n'), comment: ['# Filter Stallman copypasta on /g/:', '#/what you\'re refer+ing to as linux/i;boards:g'].join('\n'), filename: [''].join('\n'), - dimensions: ['# Highlight potential wallpapers:', '#/1920x1080/;op:yes;highlight;boards:w,wg'].join('\n'), + dimensions: ['# Highlight potential wallpapers:', '#/1920x1080/;op:yes;highlight;top:no;boards:w,wg'].join('\n'), filesize: [''].join('\n'), md5: [''].join('\n') }, @@ -534,7 +534,7 @@ filter = { filters: {}, init: function() { - var boards, filter, hl, key, op, regexp, _i, _len, _ref, _ref2, _ref3, _ref4; + var boards, filter, hl, key, op, regexp, top, _i, _len, _ref, _ref2, _ref3, _ref4, _ref5; for (key in config.filter) { this.filters[key] = []; _ref = conf[key].split('\n'); @@ -553,31 +553,34 @@ alert(e.message); continue; } - op = ((_ref3 = filter.match(/op:(yes|no|only)/)) != null ? _ref3[1].toLowerCase() : void 0) || 'no'; + op = ((_ref3 = filter.match(/[^t]op:(yes|no|only)/)) != null ? _ref3[1].toLowerCase() : void 0) || 'no'; if (hl = /highlight/.test(filter)) { hl = ((_ref4 = filter.match(/highlight:(\w+)/)) != null ? _ref4[1].toLowerCase() : void 0) || 'filter_highlight'; + top = ((_ref5 = filter.match(/top:(yes|no)/)) != null ? _ref5[1].toLowerCase() : void 0) || 'yes'; + top = top === 'yes'; } - this.filters[key].push(this.createFilter(regexp, op, hl)); + this.filters[key].push(this.createFilter(regexp, op, hl, top)); } if (!this.filters[key].length) delete this.filters[key]; } if (Object.keys(this.filters).length) return g.callbacks.push(this.node); }, - createFilter: function(regexp, op, hl) { + createFilter: function(regexp, op, hl, top) { return function(root, value, isOP) { var firstThread, thisThread; if (isOP && op === 'no' || !isOP && op === 'only') return false; if (!regexp.test(value)) return false; if (hl) { $.addClass(root, hl); - if (isOP && !g.REPLY) { + if (isOP && top && !g.REPLY) { thisThread = root.parentNode; if (firstThread = $('div[class=op]')) { $.before(firstThread.parentNode, [thisThread, thisThread.nextElementSibling]); } } return false; - } else if (isOP) { + } + if (isOP) { if (!g.REPLY) threadHiding.hideHide(root.parentNode); } else { replyHiding.hideHide(root.previousSibling); @@ -2125,7 +2128,8 @@
          You can use these settings with each regular expression, separate them with semicolons:\
        • Per boards, separate them with commas. It is global if not specified.
          For example: boards:a,jp;.
        • \
        • Filter OPs only along with their threads (`only`), replies only (`no`, this is default), or both (`yes`).
          For example: op:only;, op:no; or op:yes;.
        • \ -
        • Highlight instead of hiding. Highlighted OPs will have their threads put on top of board pages. You can specify a class name to use with a userstyle.
          For example: highlight; or hightlight:wallpaper;.
        • \ +
        • Highlight instead of hiding. You can specify a class name to use with a userstyle.
          For example: highlight; or hightlight:wallpaper;.
        • \ +
        • Highlighted OPs will have their threads put on top of board pages by default.
          For example: top:yes or top:no.
        • \
        \

        Name:

        \

        Tripcode:

        \ diff --git a/script.coffee b/script.coffee index ef0b5c4c6..a16d96d43 100644 --- a/script.coffee +++ b/script.coffee @@ -75,7 +75,7 @@ config = ].join '\n' dimensions: [ '# Highlight potential wallpapers:' - '#/1920x1080/;op:yes;highlight;boards:w,wg' + '#/1920x1080/;op:yes;highlight;top:no;boards:w,wg' ].join '\n' filesize: [ '' @@ -460,15 +460,19 @@ filter = # Filter OPs along with their threads, replies only, or both. # Defaults to replies only. - op = filter.match(/op:(yes|no|only)/)?[1].toLowerCase() or 'no' + op = filter.match(/[^t]op:(yes|no|only)/)?[1].toLowerCase() or 'no' # Highlight the post, or hide it. # If not specified, the highlight class will be filter_highlight. # Defaults to post hiding. if hl = /highlight/.test filter - hl = filter.match(/highlight:(\w+)/)?[1].toLowerCase() or 'filter_highlight' + hl = filter.match(/highlight:(\w+)/)?[1].toLowerCase() or 'filter_highlight' + # Put highlighted OP's thread on top of the board page or not. + # Defaults to on top. + top = filter.match(/top:(yes|no)/)?[1].toLowerCase() or 'yes' + top = top is 'yes' # Turn it into a boolean - @filters[key].push @createFilter regexp, op, hl + @filters[key].push @createFilter regexp, op, hl, top # Only execute filter types that contain valid filters. unless @filters[key].length @@ -477,7 +481,7 @@ filter = if Object.keys(@filters).length g.callbacks.push @node - createFilter: (regexp, op, hl) -> + createFilter: (regexp, op, hl, top) -> (root, value, isOP) -> if isOP and op is 'no' or !isOP and op is 'only' return false @@ -485,7 +489,7 @@ filter = return false if hl $.addClass root, hl - if isOP and not g.REPLY + if isOP and top and not g.REPLY # Put the highlighted OPs' threads on top of the board pages... thisThread = root.parentNode # ...before the first non highlighted thread. @@ -493,7 +497,7 @@ filter = $.before firstThread.parentNode, [thisThread, thisThread.nextElementSibling] # Continue the filter lookup to add more classes or hide it. return false - else if isOP + if isOP unless g.REPLY threadHiding.hideHide root.parentNode else @@ -1719,7 +1723,8 @@ options =
          You can use these settings with each regular expression, separate them with semicolons:
        • Per boards, separate them with commas. It is global if not specified.
          For example: boards:a,jp;.
        • Filter OPs only along with their threads (`only`), replies only (`no`, this is default), or both (`yes`).
          For example: op:only;, op:no; or op:yes;.
        • -
        • Highlight instead of hiding. Highlighted OPs will have their threads put on top of board pages. You can specify a class name to use with a userstyle.
          For example: highlight; or hightlight:wallpaper;.
        • +
        • Highlight instead of hiding. You can specify a class name to use with a userstyle.
          For example: highlight; or hightlight:wallpaper;.
        • +
        • Highlighted OPs will have their threads put on top of board pages by default.
          For example: top:yes or top:no.

        Name:

        Tripcode:

        From 6e0cd10b707936ef0bce5707ca7f21b874fc0eaa Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 15:45:08 +0100 Subject: [PATCH 27/28] Add two controversial filter examples. --- 4chan_x.user.js | 4 ++-- script.coffee | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 91ca8a838..370e5793e 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -136,8 +136,8 @@ } }, filter: { - name: [''].join('\n'), - tripcode: [''].join('\n'), + name: ['# Filter any namefags:', '#/^(?!Anonymous$)/'].join('\n'), + tripcode: ['# Filter any tripfags', '#/^!/'].join('\n'), email: ['# Filter any e-mails that are not `sage` on /a/ and /jp/:', '#/^(?!sage$)/;boards:a,jp'].join('\n'), subject: ['# Filter Generals on /v/:', '#/general/i;boards:v;op:only'].join('\n'), comment: ['# Filter Stallman copypasta on /g/:', '#/what you\'re refer+ing to as linux/i;boards:g'].join('\n'), diff --git a/script.coffee b/script.coffee index 720a5aeba..1200b539e 100644 --- a/script.coffee +++ b/script.coffee @@ -54,10 +54,12 @@ config = 'Forward Hiding': [true, 'Hide original posts of inlined backlinks'] filter: name: [ - '' + '# Filter any namefags:' + '#/^(?!Anonymous$)/' ].join '\n' tripcode: [ - '' + '# Filter any tripfags' + '#/^!/' ].join '\n' email: [ '# Filter any e-mails that are not `sage` on /a/ and /jp/:' From 718b204e7a476c8b3b939b3ac9b29601c48b4a86 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 15:46:43 +0100 Subject: [PATCH 28/28] Spaces. --- script.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script.coffee b/script.coffee index 1200b539e..344b03cfb 100644 --- a/script.coffee +++ b/script.coffee @@ -13,8 +13,8 @@ config = 'Check for Updates': [true, 'Check for updated versions of 4chan X'] Filtering: 'Anonymize': [false, 'Make everybody anonymous'] - 'Filter': [true, 'Self-moderation placebo'] - 'Recursive Filtering': [true, 'Filter replies of filtered posts, recursively'] + 'Filter': [true, 'Self-moderation placebo'] + 'Recursive Filtering': [true, 'Filter replies of filtered posts, recursively'] 'Reply Hiding': [true, 'Hide single replies'] 'Thread Hiding': [true, 'Hide entire threads'] 'Show Stubs': [true, 'Of hidden threads / replies']