From 97902bbfcc6fdbeab24c462484593dfd7c8cd45b Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 20 Feb 2012 21:36:43 +0100 Subject: [PATCH 1/8] 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 2/8] 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 c12538f2c79deb5ec883fa9538d103b1d33b0e3a Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 02:11:17 +0100 Subject: [PATCH 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 15b7a55fe01b90965f3663202b71d799456ff95e Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 21 Feb 2012 03:00:12 +0100 Subject: [PATCH 8/8] 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: ->