diff --git a/CHANGELOG.md b/CHANGELOG.md index 23a438f6f..1034221b0 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### v1.7.22 +*2014-04-27* + **ccd0** - Revert captcha fixes of 1.4.2 as Google appears to have reverted the changes on its end. This restores captcha caching. diff --git a/LICENSE b/LICENSE index 28d99d3d5..5159e8ae2 100755 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.7.21 - 2014-04-27 +* 4chan X - Version 1.7.22 - 2014-04-27 * * Licensed under the MIT license. * https://github.com/ccd0/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.meta.js b/builds/4chan-X.meta.js index 7d5613779..2004b2290 100755 --- a/builds/4chan-X.meta.js +++ b/builds/4chan-X.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.7.21 +// @version 1.7.22 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 581012d46..04122535e 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1,7 +1,7 @@ // Generated by CoffeeScript // ==UserScript== // @name 4chan X -// @version 1.7.21 +// @version 1.7.22 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -24,7 +24,7 @@ // ==/UserScript== /* -* 4chan X - Version 1.7.21 - 2014-04-27 +* 4chan X - Version 1.7.22 - 2014-04-27 * * Licensed under the MIT license. * https://github.com/ccd0/4chan-x/blob/master/LICENSE @@ -372,7 +372,7 @@ doc = d.documentElement; g = { - VERSION: '1.7.21', + VERSION: '1.7.22', NAMESPACE: '4chan X.', boards: {} }; @@ -6395,10 +6395,6 @@ onload: QR.response, onerror: function(err, url, line) { delete QR.req; - if (QR.captcha.isEnabled) { - QR.captcha.destroy(); - QR.captcha.setup(); - } post.unlock(); QR.cooldown.auto = false; QR.status(); @@ -6431,12 +6427,9 @@ return QR.status(); }, response: function() { - var URL, ban, board, err, h1, isReply, m, post, postID, postsCount, req, resDoc, threadID, _, _ref, _ref1; + var URL, ban, board, captchasCount, err, h1, isReply, m, notif, post, postID, postsCount, req, resDoc, threadID, _, _ref, _ref1; req = QR.req; delete QR.req; - if (QR.captcha.isEnabled) { - QR.captcha.destroy(); - } post = QR.posts[0]; post.unlock(); resDoc = req.response; @@ -6461,12 +6454,12 @@ } else if (/expired/i.test(err.textContent)) { err = 'This CAPTCHA is no longer valid because it has expired.'; } - QR.cooldown.auto = false; + QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : err === 'Connection error with sys.4chan.org.' ? true : false; QR.cooldown.set({ delay: 2 }); } else if (err.textContent && (m = err.textContent.match(/wait\s+(\d+)\s+second/i))) { - QR.cooldown.auto = !QR.captcha.isEnabled; + QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : true; QR.cooldown.set({ delay: m[1] }); @@ -6505,6 +6498,22 @@ }); postsCount = QR.posts.length - 1; QR.cooldown.auto = postsCount && isReply; + if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) { + notif = new Notification('Quick reply warning', { + body: "You are running low on cached captchas. Cache count: " + captchasCount + ".", + icon: Favicon.logo + }); + notif.onclick = function() { + QR.open(); + QR.captcha.nodes.input.focus(); + return window.focus(); + }; + notif.onshow = function() { + return setTimeout(function() { + return notif.close(); + }, 7 * $.SECOND); + }; + } if (!(Conf['Persistent QR'] || QR.cooldown.auto)) { QR.close(); } else { @@ -6543,11 +6552,12 @@ QR.captcha = { init: function() { - var imgContainer, input; + var container, imgContainer, input; if (d.cookie.indexOf('pass_enabled=1') >= 0) { return; } - if (!(this.isEnabled = !!$.id('captchaContainer'))) { + container = $.id('captchaContainer'); + if (!(this.isEnabled = !!container)) { return; } if (Conf['Auto-load captcha']) { @@ -6556,11 +6566,13 @@ imgContainer = $.el('div', { className: 'captcha-img', title: 'Reload reCAPTCHA', - innerHTML: '' + innerHTML: '', + hidden: true }); input = $.el('input', { className: 'captcha-input field', title: 'Verification', + placeholder: 'Focus to load reCAPTCHA', autocomplete: 'off', spellcheck: false, tabIndex: 45 @@ -6569,41 +6581,44 @@ img: imgContainer.firstChild, input: input }; + $.on(input, 'focus', this.setup); $.on(input, 'blur', QR.focusout); $.on(input, 'focus', QR.focusin); $.addClass(QR.nodes.el, 'has-captcha'); $.after(QR.nodes.com.parentNode, [imgContainer, input]); - this.beforeSetup(); - return this.afterSetup(); - }, - beforeSetup: function() { - var img, input, _ref; - _ref = this.nodes, img = _ref.img, input = _ref.input; - img.parentNode.hidden = true; - input.value = ''; - input.placeholder = 'Focus to load reCAPTCHA'; - $.on(input, 'focus', this.setup); this.setupObserver = new MutationObserver(this.afterSetup); - return this.setupObserver.observe($.id('captchaContainer'), { + this.setupObserver.observe(container, { childList: true }); + return this.afterSetup(); }, setup: function() { return $.globalEval('loadRecaptcha()'); }, afterSetup: function() { - var challenge, img, input, _ref; + var challenge, img, input, setLifetime, _ref; if (!(challenge = $.id('recaptcha_challenge_field_holder'))) { return; } QR.captcha.setupObserver.disconnect(); delete QR.captcha.setupObserver; + setLifetime = function(e) { + return QR.captcha.lifetime = e.detail; + }; + $.on(window, 'captcha:timeout', setLifetime); + $.globalEval('window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))'); + $.off(window, 'captcha:timeout', setLifetime); _ref = QR.captcha.nodes, img = _ref.img, input = _ref.input; img.parentNode.hidden = false; - input.placeholder = 'Verification'; $.off(input, 'focus', QR.captcha.setup); $.on(input, 'keydown', QR.captcha.keydown.bind(QR.captcha)); $.on(img.parentNode, 'click', QR.captcha.reload.bind(QR.captcha)); + $.get('captchas', [], function(_arg) { + var captchas; + captchas = _arg.captchas; + return QR.captcha.sync(captchas); + }); + $.sync('captchas', QR.captcha.sync); QR.captcha.nodes.challenge = challenge; new MutationObserver(QR.captcha.load.bind(QR.captcha)).observe(challenge, { childList: true, @@ -6612,31 +6627,94 @@ }); return QR.captcha.load(); }, - destroy: function() { - $.globalEval('Recaptcha.destroy()'); - return this.beforeSetup(); + sync: function(captchas) { + QR.captcha.captchas = captchas; + return QR.captcha.count(); }, getOne: function() { - var challenge, response; - challenge = this.nodes.img.alt; - response = this.nodes.input.value.trim(); - if (response && !/\s/.test(response)) { - response = "" + response + " " + response; + var captcha, challenge, response; + this.clear(); + if (captcha = this.captchas.shift()) { + challenge = captcha.challenge, response = captcha.response; + this.count(); + $.set('captchas', this.captchas); + } else { + challenge = this.nodes.img.alt; + if (response = this.nodes.input.value) { + this.reload(); + } + } + if (response) { + response = response.trim(); + if (!/\s/.test(response)) { + response = "" + response + " " + response; + } } return { challenge: challenge, response: response }; }, + save: function() { + var response; + if (!(response = this.nodes.input.value.trim())) { + return; + } + this.captchas.push({ + challenge: this.nodes.img.alt, + response: response, + timeout: this.timeout + }); + this.count(); + this.reload(); + return $.set('captchas', this.captchas); + }, + clear: function() { + var captcha, i, now, _i, _len, _ref; + if (!this.captchas.length) { + return; + } + now = Date.now(); + _ref = this.captchas; + for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { + captcha = _ref[i]; + if (captcha.timeout > now) { + break; + } + } + if (!i) { + return; + } + this.captchas = this.captchas.slice(i); + this.count(); + return $.set('captchas', this.captchas); + }, load: function() { var challenge; if (!this.nodes.challenge.firstChild) { return; } + this.timeout = Date.now() + this.lifetime * $.SECOND - $.MINUTE; challenge = this.nodes.challenge.firstChild.value; this.nodes.img.alt = challenge; this.nodes.img.src = "//www.google.com/recaptcha/api/image?c=" + challenge; - return this.nodes.input.value = null; + this.nodes.input.value = null; + return this.clear(); + }, + count: function() { + var count; + count = this.captchas ? this.captchas.length : 0; + this.nodes.input.placeholder = (function() { + switch (count) { + case 0: + return 'Verification (Shift + Enter to cache)'; + case 1: + return 'Verification (1 cached captcha)'; + default: + return "Verification (" + count + " cached captchas)"; + } + })(); + return this.nodes.input.alt = count; }, reload: function(focus) { $.globalEval('Recaptcha.reload("t")'); @@ -6647,6 +6725,8 @@ keydown: function(e) { if (e.keyCode === 8 && !this.nodes.input.value) { this.reload(); + } else if (e.keyCode === 13 && e.shiftKey) { + this.save(); } else { return; } @@ -6990,9 +7070,6 @@ node.disabled = lock; } } - if (QR.captcha.isEnabled) { - QR.captcha.nodes.input.disabled = lock; - } this.nodes.rm.style.visibility = lock ? 'hidden' : ''; (lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput); this.nodes.spoiler.disabled = lock; @@ -13388,7 +13465,6 @@ }, initReady: function() { var GMver, err, href, i, passLink, styleSelector, test, v, _i, _len, _ref, _ref1; - $.globalEval('window.recaptchaKey = "6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc"'); if ((_ref = d.title) === '4chan - Temporarily Offline' || _ref === '4chan - 404 Not Found') { if (Conf['404 Redirect'] && g.VIEW === 'thread') { href = Redirect.to('thread', { diff --git a/builds/crx.crx b/builds/crx.crx index 243a29eb8..97c29f1f4 100644 Binary files a/builds/crx.crx and b/builds/crx.crx differ diff --git a/builds/crx/manifest.json b/builds/crx/manifest.json index 9ecd35f3a..54183dc52 100755 --- a/builds/crx/manifest.json +++ b/builds/crx/manifest.json @@ -1,6 +1,6 @@ { "name": "4chan X", - "version": "1.7.21", + "version": "1.7.22", "manifest_version": 2, "description": "Cross-browser userscript for maximum lurking on 4chan.", "icons": { diff --git a/builds/crx/script.js b/builds/crx/script.js index c6ca0e2aa..3420004f7 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.7.21 - 2014-04-27 +* 4chan X - Version 1.7.22 - 2014-04-27 * * Licensed under the MIT license. * https://github.com/ccd0/4chan-x/blob/master/LICENSE @@ -348,7 +348,7 @@ doc = d.documentElement; g = { - VERSION: '1.7.21', + VERSION: '1.7.22', NAMESPACE: '4chan X.', boards: {} }; @@ -6420,10 +6420,6 @@ onload: QR.response, onerror: function(err, url, line) { delete QR.req; - if (QR.captcha.isEnabled) { - QR.captcha.destroy(); - QR.captcha.setup(); - } post.unlock(); QR.cooldown.auto = false; QR.status(); @@ -6456,12 +6452,9 @@ return QR.status(); }, response: function() { - var URL, ban, board, err, h1, isReply, m, post, postID, postsCount, req, resDoc, threadID, _, _ref, _ref1; + var URL, ban, board, captchasCount, err, h1, isReply, m, notif, post, postID, postsCount, req, resDoc, threadID, _, _ref, _ref1; req = QR.req; delete QR.req; - if (QR.captcha.isEnabled) { - QR.captcha.destroy(); - } post = QR.posts[0]; post.unlock(); resDoc = req.response; @@ -6486,12 +6479,12 @@ } else if (/expired/i.test(err.textContent)) { err = 'This CAPTCHA is no longer valid because it has expired.'; } - QR.cooldown.auto = false; + QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : err === 'Connection error with sys.4chan.org.' ? true : false; QR.cooldown.set({ delay: 2 }); } else if (err.textContent && (m = err.textContent.match(/wait\s+(\d+)\s+second/i))) { - QR.cooldown.auto = !QR.captcha.isEnabled; + QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : true; QR.cooldown.set({ delay: m[1] }); @@ -6530,6 +6523,22 @@ }); postsCount = QR.posts.length - 1; QR.cooldown.auto = postsCount && isReply; + if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) { + notif = new Notification('Quick reply warning', { + body: "You are running low on cached captchas. Cache count: " + captchasCount + ".", + icon: Favicon.logo + }); + notif.onclick = function() { + QR.open(); + QR.captcha.nodes.input.focus(); + return window.focus(); + }; + notif.onshow = function() { + return setTimeout(function() { + return notif.close(); + }, 7 * $.SECOND); + }; + } if (!(Conf['Persistent QR'] || QR.cooldown.auto)) { QR.close(); } else { @@ -6568,11 +6577,12 @@ QR.captcha = { init: function() { - var imgContainer, input; + var container, imgContainer, input; if (d.cookie.indexOf('pass_enabled=1') >= 0) { return; } - if (!(this.isEnabled = !!$.id('captchaContainer'))) { + container = $.id('captchaContainer'); + if (!(this.isEnabled = !!container)) { return; } if (Conf['Auto-load captcha']) { @@ -6581,11 +6591,13 @@ imgContainer = $.el('div', { className: 'captcha-img', title: 'Reload reCAPTCHA', - innerHTML: '' + innerHTML: '', + hidden: true }); input = $.el('input', { className: 'captcha-input field', title: 'Verification', + placeholder: 'Focus to load reCAPTCHA', autocomplete: 'off', spellcheck: false, tabIndex: 45 @@ -6594,41 +6606,44 @@ img: imgContainer.firstChild, input: input }; + $.on(input, 'focus', this.setup); $.on(input, 'blur', QR.focusout); $.on(input, 'focus', QR.focusin); $.addClass(QR.nodes.el, 'has-captcha'); $.after(QR.nodes.com.parentNode, [imgContainer, input]); - this.beforeSetup(); - return this.afterSetup(); - }, - beforeSetup: function() { - var img, input, _ref; - _ref = this.nodes, img = _ref.img, input = _ref.input; - img.parentNode.hidden = true; - input.value = ''; - input.placeholder = 'Focus to load reCAPTCHA'; - $.on(input, 'focus', this.setup); this.setupObserver = new MutationObserver(this.afterSetup); - return this.setupObserver.observe($.id('captchaContainer'), { + this.setupObserver.observe(container, { childList: true }); + return this.afterSetup(); }, setup: function() { return $.globalEval('loadRecaptcha()'); }, afterSetup: function() { - var challenge, img, input, _ref; + var challenge, img, input, setLifetime, _ref; if (!(challenge = $.id('recaptcha_challenge_field_holder'))) { return; } QR.captcha.setupObserver.disconnect(); delete QR.captcha.setupObserver; + setLifetime = function(e) { + return QR.captcha.lifetime = e.detail; + }; + $.on(window, 'captcha:timeout', setLifetime); + $.globalEval('window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))'); + $.off(window, 'captcha:timeout', setLifetime); _ref = QR.captcha.nodes, img = _ref.img, input = _ref.input; img.parentNode.hidden = false; - input.placeholder = 'Verification'; $.off(input, 'focus', QR.captcha.setup); $.on(input, 'keydown', QR.captcha.keydown.bind(QR.captcha)); $.on(img.parentNode, 'click', QR.captcha.reload.bind(QR.captcha)); + $.get('captchas', [], function(_arg) { + var captchas; + captchas = _arg.captchas; + return QR.captcha.sync(captchas); + }); + $.sync('captchas', QR.captcha.sync); QR.captcha.nodes.challenge = challenge; new MutationObserver(QR.captcha.load.bind(QR.captcha)).observe(challenge, { childList: true, @@ -6637,31 +6652,94 @@ }); return QR.captcha.load(); }, - destroy: function() { - $.globalEval('Recaptcha.destroy()'); - return this.beforeSetup(); + sync: function(captchas) { + QR.captcha.captchas = captchas; + return QR.captcha.count(); }, getOne: function() { - var challenge, response; - challenge = this.nodes.img.alt; - response = this.nodes.input.value.trim(); - if (response && !/\s/.test(response)) { - response = "" + response + " " + response; + var captcha, challenge, response; + this.clear(); + if (captcha = this.captchas.shift()) { + challenge = captcha.challenge, response = captcha.response; + this.count(); + $.set('captchas', this.captchas); + } else { + challenge = this.nodes.img.alt; + if (response = this.nodes.input.value) { + this.reload(); + } + } + if (response) { + response = response.trim(); + if (!/\s/.test(response)) { + response = "" + response + " " + response; + } } return { challenge: challenge, response: response }; }, + save: function() { + var response; + if (!(response = this.nodes.input.value.trim())) { + return; + } + this.captchas.push({ + challenge: this.nodes.img.alt, + response: response, + timeout: this.timeout + }); + this.count(); + this.reload(); + return $.set('captchas', this.captchas); + }, + clear: function() { + var captcha, i, now, _i, _len, _ref; + if (!this.captchas.length) { + return; + } + now = Date.now(); + _ref = this.captchas; + for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { + captcha = _ref[i]; + if (captcha.timeout > now) { + break; + } + } + if (!i) { + return; + } + this.captchas = this.captchas.slice(i); + this.count(); + return $.set('captchas', this.captchas); + }, load: function() { var challenge; if (!this.nodes.challenge.firstChild) { return; } + this.timeout = Date.now() + this.lifetime * $.SECOND - $.MINUTE; challenge = this.nodes.challenge.firstChild.value; this.nodes.img.alt = challenge; this.nodes.img.src = "//www.google.com/recaptcha/api/image?c=" + challenge; - return this.nodes.input.value = null; + this.nodes.input.value = null; + return this.clear(); + }, + count: function() { + var count; + count = this.captchas ? this.captchas.length : 0; + this.nodes.input.placeholder = (function() { + switch (count) { + case 0: + return 'Verification (Shift + Enter to cache)'; + case 1: + return 'Verification (1 cached captcha)'; + default: + return "Verification (" + count + " cached captchas)"; + } + })(); + return this.nodes.input.alt = count; }, reload: function(focus) { $.globalEval('Recaptcha.reload("t")'); @@ -6672,6 +6750,8 @@ keydown: function(e) { if (e.keyCode === 8 && !this.nodes.input.value) { this.reload(); + } else if (e.keyCode === 13 && e.shiftKey) { + this.save(); } else { return; } @@ -7009,9 +7089,6 @@ node.disabled = lock; } } - if (QR.captcha.isEnabled) { - QR.captcha.nodes.input.disabled = lock; - } this.nodes.rm.style.visibility = lock ? 'hidden' : ''; (lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput); this.nodes.spoiler.disabled = lock; @@ -13388,7 +13465,6 @@ }, initReady: function() { var err, href, passLink, styleSelector, _ref; - $.globalEval('window.recaptchaKey = "6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc"'); if ((_ref = d.title) === '4chan - Temporarily Offline' || _ref === '4chan - 404 Not Found') { if (Conf['404 Redirect'] && g.VIEW === 'thread') { href = Redirect.to('thread', { diff --git a/builds/updates.xml b/builds/updates.xml index ac3495ec9..8c1cf6839 100644 --- a/builds/updates.xml +++ b/builds/updates.xml @@ -1,7 +1,7 @@ - + diff --git a/package.json b/package.json index e5a23685b..ebff82fc9 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "4chan-X", - "version": "1.7.21", + "version": "1.7.22", "description": "Cross-browser userscript for maximum lurking on 4chan.", "meta": { "name": "4chan X",