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",