Merge branch 'v3'
Conflicts: CHANGELOG.md LICENSE builds/appchan-x.user.js builds/crx/script.js src/Posting/QR.captcha.coffee
This commit is contained in:
commit
9f7026e419
@ -1,4 +1,9 @@
|
|||||||
### v2.9.9
|
**MayhemYDG**:
|
||||||
|
- Fix captcha submission:
|
||||||
|
Captchas were reloaded the instant a post was submitted to 4chan. Unfortunately, a recent change to reCAPTCHA made it so reloading captchas invalidates the ones that loaded but not yet used. This is now fixed by only unloading the captcha, and only load new ones after the post is submitted.<br>
|
||||||
|
This also kills captcha caching, so the feature was removed.
|
||||||
|
|
||||||
|
### v2.9.9
|
||||||
*2014-03-27*
|
*2014-03-27*
|
||||||
|
|
||||||
**MayhemYDG**:
|
**MayhemYDG**:
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* appchan x - Version 2.9.9 - 2014-03-27
|
* appchan x - Version 2.9.9 - 2014-04-02
|
||||||
*
|
*
|
||||||
* Licensed under the MIT license.
|
* Licensed under the MIT license.
|
||||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* appchan x - Version 2.9.9 - 2014-03-27
|
* appchan x - Version 2.9.9 - 2014-04-02
|
||||||
*
|
*
|
||||||
* Licensed under the MIT license.
|
* Licensed under the MIT license.
|
||||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||||
@ -8063,6 +8063,9 @@
|
|||||||
if (Conf['Comment Expansion']) {
|
if (Conf['Comment Expansion']) {
|
||||||
ExpandComment.callbacks.push(this.node);
|
ExpandComment.callbacks.push(this.node);
|
||||||
}
|
}
|
||||||
|
if (Conf['Embedding'] || Conf['Link Title']) {
|
||||||
|
this.embedProcess = Function('link', "var data = this.services(link); if (data) { " + ((Conf['Embedding'] ? 'this.embed(data);\n' : '') + (Conf['Title Link'] ? 'this.title(data);' : '')) + " }");
|
||||||
|
}
|
||||||
return Post.callbacks.push({
|
return Post.callbacks.push({
|
||||||
name: 'Linkify',
|
name: 'Linkify',
|
||||||
cb: this.node
|
cb: this.node
|
||||||
@ -8132,17 +8135,7 @@
|
|||||||
Linkify.embedProcess(Linkify.makeLink(link, this));
|
Linkify.embedProcess(Linkify.makeLink(link, this));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
embedProcess: function(link) {
|
embedProcess: function() {},
|
||||||
var data;
|
|
||||||
if (data = Linkify.services(link)) {
|
|
||||||
if (Conf['Embedding']) {
|
|
||||||
Linkify.embed(data);
|
|
||||||
}
|
|
||||||
if (Conf['Link Title']) {
|
|
||||||
return Linkify.title(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
regString: /((https?|mailto|git|magnet|ftp|irc):([a-z\d%\/])|[-a-z\d]+[.](aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2})([:\/]|(?!.))|[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}|[-\w\d.@]+@[a-z\d.-]+\.[a-z\d])/i,
|
regString: /((https?|mailto|git|magnet|ftp|irc):([a-z\d%\/])|[-a-z\d]+[.](aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2})([:\/]|(?!.))|[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}|[-\w\d.@]+@[a-z\d.-]+\.[a-z\d])/i,
|
||||||
makeRange: function(startNode, endNode, startOffset, endOffset) {
|
makeRange: function(startNode, endNode, startOffset, endOffset) {
|
||||||
var range;
|
var range;
|
||||||
@ -9354,6 +9347,10 @@
|
|||||||
onload: QR.response,
|
onload: QR.response,
|
||||||
onerror: function() {
|
onerror: function() {
|
||||||
delete QR.req;
|
delete QR.req;
|
||||||
|
if (QR.captcha.isEnabled) {
|
||||||
|
QR.captcha.destroy();
|
||||||
|
QR.captcha.setup();
|
||||||
|
}
|
||||||
post.unlock();
|
post.unlock();
|
||||||
QR.cooldown.auto = false;
|
QR.cooldown.auto = false;
|
||||||
QR.status();
|
QR.status();
|
||||||
@ -9383,9 +9380,12 @@
|
|||||||
return QR.status();
|
return QR.status();
|
||||||
},
|
},
|
||||||
response: function() {
|
response: function() {
|
||||||
var URL, ban, board, captchasCount, err, h1, isReply, m, notif, post, postID, postsCount, req, resDoc, threadID, _, _ref, _ref1;
|
var URL, ban, board, err, h1, isReply, m, post, postID, postsCount, req, resDoc, threadID, _, _ref, _ref1;
|
||||||
req = QR.req;
|
req = QR.req;
|
||||||
delete QR.req;
|
delete QR.req;
|
||||||
|
if (QR.captcha.isEnabled) {
|
||||||
|
QR.captcha.destroy();
|
||||||
|
}
|
||||||
post = QR.posts[0];
|
post = QR.posts[0];
|
||||||
post.unlock();
|
post.unlock();
|
||||||
resDoc = req.response;
|
resDoc = req.response;
|
||||||
@ -9407,15 +9407,13 @@
|
|||||||
if (/captcha|verification/i.test(err.textContent) || err === 'Connection error with sys.4chan.org.') {
|
if (/captcha|verification/i.test(err.textContent) || err === 'Connection error with sys.4chan.org.') {
|
||||||
if (/mistyped/i.test(err.textContent)) {
|
if (/mistyped/i.test(err.textContent)) {
|
||||||
err = 'You seem to have mistyped the CAPTCHA.';
|
err = 'You seem to have mistyped the CAPTCHA.';
|
||||||
} else if (/expired/i.test(err.textContent)) {
|
|
||||||
err = 'This CAPTCHA is no longer valid because it has expired.';
|
|
||||||
}
|
}
|
||||||
QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : err === 'Connection error with sys.4chan.org.' ? true : false;
|
QR.cooldown.auto = false;
|
||||||
QR.cooldown.set({
|
QR.cooldown.set({
|
||||||
delay: 2
|
delay: 2
|
||||||
});
|
});
|
||||||
} else if (err.textContent && (m = err.textContent.match(/wait\s+(\d+)\s+second/i))) {
|
} else if (err.textContent && (m = err.textContent.match(/wait\s+(\d+)\s+second/i))) {
|
||||||
QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : true;
|
QR.cooldown.auto = !QR.captcha.isEnabled;
|
||||||
QR.cooldown.set({
|
QR.cooldown.set({
|
||||||
delay: m[1]
|
delay: m[1]
|
||||||
});
|
});
|
||||||
@ -9454,22 +9452,6 @@
|
|||||||
});
|
});
|
||||||
postsCount = QR.posts.length - 1;
|
postsCount = QR.posts.length - 1;
|
||||||
QR.cooldown.auto = postsCount && isReply;
|
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)) {
|
if (!(Conf['Persistent QR'] || QR.cooldown.auto)) {
|
||||||
QR.close();
|
QR.close();
|
||||||
} else {
|
} else {
|
||||||
@ -9525,24 +9507,21 @@
|
|||||||
|
|
||||||
QR.captcha = {
|
QR.captcha = {
|
||||||
init: function() {
|
init: function() {
|
||||||
var container, imgContainer, input;
|
var imgContainer, input;
|
||||||
if (d.cookie.indexOf('pass_enabled=1') >= 0) {
|
if (d.cookie.indexOf('pass_enabled=1') >= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
container = $.id('captchaContainer');
|
if (!(this.isEnabled = !!$.id('captchaContainer'))) {
|
||||||
if (!(this.isEnabled = !!container)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
imgContainer = $.el('div', {
|
imgContainer = $.el('div', {
|
||||||
className: 'captcha-img',
|
className: 'captcha-img',
|
||||||
title: 'Reload reCAPTCHA',
|
title: 'Reload reCAPTCHA',
|
||||||
innerHTML: '<div><img></div>',
|
innerHTML: '<div><img></div>'
|
||||||
hidden: true
|
|
||||||
});
|
});
|
||||||
input = $.el('input', {
|
input = $.el('input', {
|
||||||
className: 'captcha-input field',
|
className: 'captcha-input field',
|
||||||
title: 'Verification',
|
title: 'Verification',
|
||||||
placeholder: 'Focus to load reCAPTCHA',
|
|
||||||
autocomplete: 'off',
|
autocomplete: 'off',
|
||||||
spellcheck: false,
|
spellcheck: false,
|
||||||
tabIndex: 45
|
tabIndex: 45
|
||||||
@ -9551,47 +9530,41 @@
|
|||||||
img: imgContainer.firstChild.firstChild,
|
img: imgContainer.firstChild.firstChild,
|
||||||
input: input
|
input: input
|
||||||
};
|
};
|
||||||
$.on(input, 'focus', this.setup);
|
|
||||||
$.on(input, 'blur', QR.focusout);
|
$.on(input, 'blur', QR.focusout);
|
||||||
$.on(input, 'focus', QR.focusin);
|
$.on(input, 'focus', QR.focusin);
|
||||||
$.addClass(QR.nodes.el, 'has-captcha');
|
$.addClass(QR.nodes.el, 'has-captcha');
|
||||||
$.after(QR.nodes.com.parentNode, [imgContainer, input]);
|
$.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.parentNode.hidden = true;
|
||||||
|
input.value = '';
|
||||||
|
input.placeholder = 'Focus to load reCAPTCHA';
|
||||||
|
$.on(input, 'focus', this.setup);
|
||||||
this.setupObserver = new MutationObserver(this.afterSetup);
|
this.setupObserver = new MutationObserver(this.afterSetup);
|
||||||
this.setupObserver.observe(container, {
|
return this.setupObserver.observe($.id('captchaContainer'), {
|
||||||
childList: true
|
childList: true
|
||||||
});
|
});
|
||||||
if (Conf['Auto-load captcha']) {
|
|
||||||
this.setup();
|
|
||||||
}
|
|
||||||
return this.afterSetup();
|
|
||||||
},
|
},
|
||||||
setup: function() {
|
setup: function() {
|
||||||
return $.globalEval('loadRecaptcha()');
|
return $.globalEval('loadRecaptcha()');
|
||||||
},
|
},
|
||||||
afterSetup: function() {
|
afterSetup: function() {
|
||||||
var challenge, img, input, setLifetime, _ref;
|
var challenge, img, input, _ref;
|
||||||
if (!(challenge = $.id('recaptcha_challenge_field_holder'))) {
|
if (!(challenge = $.id('recaptcha_challenge_field_holder'))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QR.captcha.setupObserver.disconnect();
|
QR.captcha.setupObserver.disconnect();
|
||||||
delete QR.captcha.setupObserver;
|
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;
|
_ref = QR.captcha.nodes, img = _ref.img, input = _ref.input;
|
||||||
img.parentNode.parentNode.hidden = false;
|
img.parentNode.parentNode.hidden = false;
|
||||||
|
input.placeholder = 'Verification';
|
||||||
$.off(input, 'focus', QR.captcha.setup);
|
$.off(input, 'focus', QR.captcha.setup);
|
||||||
$.on(input, 'keydown', QR.captcha.keydown.bind(QR.captcha));
|
$.on(input, 'keydown', QR.captcha.keydown.bind(QR.captcha));
|
||||||
$.on(img.parentNode, 'click', QR.captcha.reload.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;
|
QR.captcha.nodes.challenge = challenge;
|
||||||
new MutationObserver(QR.captcha.load.bind(QR.captcha)).observe(challenge, {
|
new MutationObserver(QR.captcha.load.bind(QR.captcha)).observe(challenge, {
|
||||||
childList: true,
|
childList: true,
|
||||||
@ -9600,94 +9573,31 @@
|
|||||||
});
|
});
|
||||||
return QR.captcha.load();
|
return QR.captcha.load();
|
||||||
},
|
},
|
||||||
sync: function(captchas) {
|
destroy: function() {
|
||||||
QR.captcha.captchas = captchas;
|
$.globalEval('Recaptcha.destroy()');
|
||||||
return QR.captcha.count();
|
return this.beforeSetup();
|
||||||
},
|
},
|
||||||
getOne: function() {
|
getOne: function() {
|
||||||
var captcha, challenge, response;
|
var challenge, response;
|
||||||
this.clear();
|
challenge = this.nodes.img.alt;
|
||||||
if (captcha = this.captchas.shift()) {
|
response = this.nodes.input.value.trim();
|
||||||
challenge = captcha.challenge, response = captcha.response;
|
if (response && !/\s/.test(response)) {
|
||||||
this.count();
|
response = "" + response + " " + response;
|
||||||
$.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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
challenge: challenge,
|
challenge: challenge,
|
||||||
response: response
|
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) {
|
|
||||||
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() {
|
load: function() {
|
||||||
var challenge;
|
var challenge;
|
||||||
if (!this.nodes.challenge.firstChild) {
|
if (!this.nodes.challenge.firstChild) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.timeout = Date.now() + this.lifetime * $.SECOND - $.MINUTE;
|
|
||||||
challenge = this.nodes.challenge.firstChild.value;
|
challenge = this.nodes.challenge.firstChild.value;
|
||||||
this.nodes.img.alt = challenge;
|
this.nodes.img.alt = challenge;
|
||||||
this.nodes.img.src = "//www.google.com/recaptcha/api/image?c=" + challenge;
|
this.nodes.img.src = "//www.google.com/recaptcha/api/image?c=" + challenge;
|
||||||
this.nodes.input.value = null;
|
return 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) {
|
reload: function(focus) {
|
||||||
$.globalEval('Recaptcha.reload("t")');
|
$.globalEval('Recaptcha.reload("t")');
|
||||||
@ -9698,8 +9608,6 @@
|
|||||||
keydown: function(e) {
|
keydown: function(e) {
|
||||||
if (e.keyCode === 8 && !this.nodes.input.value) {
|
if (e.keyCode === 8 && !this.nodes.input.value) {
|
||||||
this.reload();
|
this.reload();
|
||||||
} else if (e.keyCode === 13 && e.shiftKey) {
|
|
||||||
this.save();
|
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -10031,6 +9939,9 @@
|
|||||||
node.disabled = lock;
|
node.disabled = lock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (QR.captcha.isEnabled) {
|
||||||
|
QR.captcha.nodes.input.disabled = lock;
|
||||||
|
}
|
||||||
this.nodes.rm.style.visibility = lock ? 'hidden' : '';
|
this.nodes.rm.style.visibility = lock ? 'hidden' : '';
|
||||||
(lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput);
|
(lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput);
|
||||||
this.nodes.spoiler.disabled = lock;
|
this.nodes.spoiler.disabled = lock;
|
||||||
@ -16133,18 +16044,18 @@
|
|||||||
if (this.isClone) {
|
if (this.isClone) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return this.nodes.date.textContent = Time.funk(Time, this.info.date);
|
return this.nodes.date.textContent = Time.funk(this.info.date);
|
||||||
},
|
},
|
||||||
createFunc: function(format) {
|
createFunc: function(format) {
|
||||||
var code;
|
var code;
|
||||||
code = format.replace(/%([A-Za-z])/g, function(s, c) {
|
code = format.replace(/%([A-Za-z])/g, function(s, c) {
|
||||||
if (c in Time.formatters) {
|
if (c in Time.formatters) {
|
||||||
return "' + Time.formatters." + c + ".call(date) + '";
|
return "' + this.formatters." + c + ".call(date) + '";
|
||||||
} else {
|
} else {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Function('Time', 'date', "return '" + code + "'");
|
return Function('date', "return '" + code + "'");
|
||||||
},
|
},
|
||||||
day: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
day: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
||||||
month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Generated by CoffeeScript
|
// Generated by CoffeeScript
|
||||||
/*
|
/*
|
||||||
* appchan x - Version 2.9.9 - 2014-03-27
|
* appchan x - Version 2.9.9 - 2014-04-02
|
||||||
*
|
*
|
||||||
* Licensed under the MIT license.
|
* Licensed under the MIT license.
|
||||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||||
@ -8116,6 +8116,9 @@
|
|||||||
if (Conf['Comment Expansion']) {
|
if (Conf['Comment Expansion']) {
|
||||||
ExpandComment.callbacks.push(this.node);
|
ExpandComment.callbacks.push(this.node);
|
||||||
}
|
}
|
||||||
|
if (Conf['Embedding'] || Conf['Link Title']) {
|
||||||
|
this.embedProcess = Function('link', "var data = this.services(link); if (data) { " + ((Conf['Embedding'] ? 'this.embed(data);\n' : '') + (Conf['Title Link'] ? 'this.title(data);' : '')) + " }");
|
||||||
|
}
|
||||||
return Post.callbacks.push({
|
return Post.callbacks.push({
|
||||||
name: 'Linkify',
|
name: 'Linkify',
|
||||||
cb: this.node
|
cb: this.node
|
||||||
@ -8185,17 +8188,7 @@
|
|||||||
Linkify.embedProcess(Linkify.makeLink(link, this));
|
Linkify.embedProcess(Linkify.makeLink(link, this));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
embedProcess: function(link) {
|
embedProcess: function() {},
|
||||||
var data;
|
|
||||||
if (data = Linkify.services(link)) {
|
|
||||||
if (Conf['Embedding']) {
|
|
||||||
Linkify.embed(data);
|
|
||||||
}
|
|
||||||
if (Conf['Link Title']) {
|
|
||||||
return Linkify.title(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
regString: /((https?|mailto|git|magnet|ftp|irc):([a-z\d%\/])|[-a-z\d]+[.](aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2})([:\/]|(?!.))|[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}|[-\w\d.@]+@[a-z\d.-]+\.[a-z\d])/i,
|
regString: /((https?|mailto|git|magnet|ftp|irc):([a-z\d%\/])|[-a-z\d]+[.](aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2})([:\/]|(?!.))|[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}|[-\w\d.@]+@[a-z\d.-]+\.[a-z\d])/i,
|
||||||
makeRange: function(startNode, endNode, startOffset, endOffset) {
|
makeRange: function(startNode, endNode, startOffset, endOffset) {
|
||||||
var range;
|
var range;
|
||||||
@ -9398,6 +9391,10 @@
|
|||||||
onload: QR.response,
|
onload: QR.response,
|
||||||
onerror: function() {
|
onerror: function() {
|
||||||
delete QR.req;
|
delete QR.req;
|
||||||
|
if (QR.captcha.isEnabled) {
|
||||||
|
QR.captcha.destroy();
|
||||||
|
QR.captcha.setup();
|
||||||
|
}
|
||||||
post.unlock();
|
post.unlock();
|
||||||
QR.cooldown.auto = false;
|
QR.cooldown.auto = false;
|
||||||
QR.status();
|
QR.status();
|
||||||
@ -9427,9 +9424,12 @@
|
|||||||
return QR.status();
|
return QR.status();
|
||||||
},
|
},
|
||||||
response: function() {
|
response: function() {
|
||||||
var URL, ban, board, captchasCount, err, h1, isReply, m, notif, post, postID, postsCount, req, resDoc, threadID, _, _ref, _ref1;
|
var URL, ban, board, err, h1, isReply, m, post, postID, postsCount, req, resDoc, threadID, _, _ref, _ref1;
|
||||||
req = QR.req;
|
req = QR.req;
|
||||||
delete QR.req;
|
delete QR.req;
|
||||||
|
if (QR.captcha.isEnabled) {
|
||||||
|
QR.captcha.destroy();
|
||||||
|
}
|
||||||
post = QR.posts[0];
|
post = QR.posts[0];
|
||||||
post.unlock();
|
post.unlock();
|
||||||
resDoc = req.response;
|
resDoc = req.response;
|
||||||
@ -9451,15 +9451,13 @@
|
|||||||
if (/captcha|verification/i.test(err.textContent) || err === 'Connection error with sys.4chan.org.') {
|
if (/captcha|verification/i.test(err.textContent) || err === 'Connection error with sys.4chan.org.') {
|
||||||
if (/mistyped/i.test(err.textContent)) {
|
if (/mistyped/i.test(err.textContent)) {
|
||||||
err = 'You seem to have mistyped the CAPTCHA.';
|
err = 'You seem to have mistyped the CAPTCHA.';
|
||||||
} else if (/expired/i.test(err.textContent)) {
|
|
||||||
err = 'This CAPTCHA is no longer valid because it has expired.';
|
|
||||||
}
|
}
|
||||||
QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : err === 'Connection error with sys.4chan.org.' ? true : false;
|
QR.cooldown.auto = false;
|
||||||
QR.cooldown.set({
|
QR.cooldown.set({
|
||||||
delay: 2
|
delay: 2
|
||||||
});
|
});
|
||||||
} else if (err.textContent && (m = err.textContent.match(/wait\s+(\d+)\s+second/i))) {
|
} else if (err.textContent && (m = err.textContent.match(/wait\s+(\d+)\s+second/i))) {
|
||||||
QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : true;
|
QR.cooldown.auto = !QR.captcha.isEnabled;
|
||||||
QR.cooldown.set({
|
QR.cooldown.set({
|
||||||
delay: m[1]
|
delay: m[1]
|
||||||
});
|
});
|
||||||
@ -9498,22 +9496,6 @@
|
|||||||
});
|
});
|
||||||
postsCount = QR.posts.length - 1;
|
postsCount = QR.posts.length - 1;
|
||||||
QR.cooldown.auto = postsCount && isReply;
|
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)) {
|
if (!(Conf['Persistent QR'] || QR.cooldown.auto)) {
|
||||||
QR.close();
|
QR.close();
|
||||||
} else {
|
} else {
|
||||||
@ -9569,24 +9551,21 @@
|
|||||||
|
|
||||||
QR.captcha = {
|
QR.captcha = {
|
||||||
init: function() {
|
init: function() {
|
||||||
var container, imgContainer, input;
|
var imgContainer, input;
|
||||||
if (d.cookie.indexOf('pass_enabled=1') >= 0) {
|
if (d.cookie.indexOf('pass_enabled=1') >= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
container = $.id('captchaContainer');
|
if (!(this.isEnabled = !!$.id('captchaContainer'))) {
|
||||||
if (!(this.isEnabled = !!container)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
imgContainer = $.el('div', {
|
imgContainer = $.el('div', {
|
||||||
className: 'captcha-img',
|
className: 'captcha-img',
|
||||||
title: 'Reload reCAPTCHA',
|
title: 'Reload reCAPTCHA',
|
||||||
innerHTML: '<div><img></div>',
|
innerHTML: '<div><img></div>'
|
||||||
hidden: true
|
|
||||||
});
|
});
|
||||||
input = $.el('input', {
|
input = $.el('input', {
|
||||||
className: 'captcha-input field',
|
className: 'captcha-input field',
|
||||||
title: 'Verification',
|
title: 'Verification',
|
||||||
placeholder: 'Focus to load reCAPTCHA',
|
|
||||||
autocomplete: 'off',
|
autocomplete: 'off',
|
||||||
spellcheck: false,
|
spellcheck: false,
|
||||||
tabIndex: 45
|
tabIndex: 45
|
||||||
@ -9595,47 +9574,41 @@
|
|||||||
img: imgContainer.firstChild.firstChild,
|
img: imgContainer.firstChild.firstChild,
|
||||||
input: input
|
input: input
|
||||||
};
|
};
|
||||||
$.on(input, 'focus', this.setup);
|
|
||||||
$.on(input, 'blur', QR.focusout);
|
$.on(input, 'blur', QR.focusout);
|
||||||
$.on(input, 'focus', QR.focusin);
|
$.on(input, 'focus', QR.focusin);
|
||||||
$.addClass(QR.nodes.el, 'has-captcha');
|
$.addClass(QR.nodes.el, 'has-captcha');
|
||||||
$.after(QR.nodes.com.parentNode, [imgContainer, input]);
|
$.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.parentNode.hidden = true;
|
||||||
|
input.value = '';
|
||||||
|
input.placeholder = 'Focus to load reCAPTCHA';
|
||||||
|
$.on(input, 'focus', this.setup);
|
||||||
this.setupObserver = new MutationObserver(this.afterSetup);
|
this.setupObserver = new MutationObserver(this.afterSetup);
|
||||||
this.setupObserver.observe(container, {
|
return this.setupObserver.observe($.id('captchaContainer'), {
|
||||||
childList: true
|
childList: true
|
||||||
});
|
});
|
||||||
if (Conf['Auto-load captcha']) {
|
|
||||||
this.setup();
|
|
||||||
}
|
|
||||||
return this.afterSetup();
|
|
||||||
},
|
},
|
||||||
setup: function() {
|
setup: function() {
|
||||||
return $.globalEval('loadRecaptcha()');
|
return $.globalEval('loadRecaptcha()');
|
||||||
},
|
},
|
||||||
afterSetup: function() {
|
afterSetup: function() {
|
||||||
var challenge, img, input, setLifetime, _ref;
|
var challenge, img, input, _ref;
|
||||||
if (!(challenge = $.id('recaptcha_challenge_field_holder'))) {
|
if (!(challenge = $.id('recaptcha_challenge_field_holder'))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QR.captcha.setupObserver.disconnect();
|
QR.captcha.setupObserver.disconnect();
|
||||||
delete QR.captcha.setupObserver;
|
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;
|
_ref = QR.captcha.nodes, img = _ref.img, input = _ref.input;
|
||||||
img.parentNode.parentNode.hidden = false;
|
img.parentNode.parentNode.hidden = false;
|
||||||
|
input.placeholder = 'Verification';
|
||||||
$.off(input, 'focus', QR.captcha.setup);
|
$.off(input, 'focus', QR.captcha.setup);
|
||||||
$.on(input, 'keydown', QR.captcha.keydown.bind(QR.captcha));
|
$.on(input, 'keydown', QR.captcha.keydown.bind(QR.captcha));
|
||||||
$.on(img.parentNode, 'click', QR.captcha.reload.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;
|
QR.captcha.nodes.challenge = challenge;
|
||||||
new MutationObserver(QR.captcha.load.bind(QR.captcha)).observe(challenge, {
|
new MutationObserver(QR.captcha.load.bind(QR.captcha)).observe(challenge, {
|
||||||
childList: true,
|
childList: true,
|
||||||
@ -9644,94 +9617,31 @@
|
|||||||
});
|
});
|
||||||
return QR.captcha.load();
|
return QR.captcha.load();
|
||||||
},
|
},
|
||||||
sync: function(captchas) {
|
destroy: function() {
|
||||||
QR.captcha.captchas = captchas;
|
$.globalEval('Recaptcha.destroy()');
|
||||||
return QR.captcha.count();
|
return this.beforeSetup();
|
||||||
},
|
},
|
||||||
getOne: function() {
|
getOne: function() {
|
||||||
var captcha, challenge, response;
|
var challenge, response;
|
||||||
this.clear();
|
challenge = this.nodes.img.alt;
|
||||||
if (captcha = this.captchas.shift()) {
|
response = this.nodes.input.value.trim();
|
||||||
challenge = captcha.challenge, response = captcha.response;
|
if (response && !/\s/.test(response)) {
|
||||||
this.count();
|
response = "" + response + " " + response;
|
||||||
$.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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
challenge: challenge,
|
challenge: challenge,
|
||||||
response: response
|
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) {
|
|
||||||
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() {
|
load: function() {
|
||||||
var challenge;
|
var challenge;
|
||||||
if (!this.nodes.challenge.firstChild) {
|
if (!this.nodes.challenge.firstChild) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.timeout = Date.now() + this.lifetime * $.SECOND - $.MINUTE;
|
|
||||||
challenge = this.nodes.challenge.firstChild.value;
|
challenge = this.nodes.challenge.firstChild.value;
|
||||||
this.nodes.img.alt = challenge;
|
this.nodes.img.alt = challenge;
|
||||||
this.nodes.img.src = "//www.google.com/recaptcha/api/image?c=" + challenge;
|
this.nodes.img.src = "//www.google.com/recaptcha/api/image?c=" + challenge;
|
||||||
this.nodes.input.value = null;
|
return 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) {
|
reload: function(focus) {
|
||||||
$.globalEval('Recaptcha.reload("t")');
|
$.globalEval('Recaptcha.reload("t")');
|
||||||
@ -9742,8 +9652,6 @@
|
|||||||
keydown: function(e) {
|
keydown: function(e) {
|
||||||
if (e.keyCode === 8 && !this.nodes.input.value) {
|
if (e.keyCode === 8 && !this.nodes.input.value) {
|
||||||
this.reload();
|
this.reload();
|
||||||
} else if (e.keyCode === 13 && e.shiftKey) {
|
|
||||||
this.save();
|
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -10069,6 +9977,9 @@
|
|||||||
node.disabled = lock;
|
node.disabled = lock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (QR.captcha.isEnabled) {
|
||||||
|
QR.captcha.nodes.input.disabled = lock;
|
||||||
|
}
|
||||||
this.nodes.rm.style.visibility = lock ? 'hidden' : '';
|
this.nodes.rm.style.visibility = lock ? 'hidden' : '';
|
||||||
(lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput);
|
(lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput);
|
||||||
this.nodes.spoiler.disabled = lock;
|
this.nodes.spoiler.disabled = lock;
|
||||||
@ -16154,18 +16065,18 @@
|
|||||||
if (this.isClone) {
|
if (this.isClone) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return this.nodes.date.textContent = Time.funk(Time, this.info.date);
|
return this.nodes.date.textContent = Time.funk(this.info.date);
|
||||||
},
|
},
|
||||||
createFunc: function(format) {
|
createFunc: function(format) {
|
||||||
var code;
|
var code;
|
||||||
code = format.replace(/%([A-Za-z])/g, function(s, c) {
|
code = format.replace(/%([A-Za-z])/g, function(s, c) {
|
||||||
if (c in Time.formatters) {
|
if (c in Time.formatters) {
|
||||||
return "' + Time.formatters." + c + ".call(date) + '";
|
return "' + this.formatters." + c + ".call(date) + '";
|
||||||
} else {
|
} else {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Function('Time', 'date', "return '" + code + "'");
|
return Function('date', "return '" + code + "'");
|
||||||
},
|
},
|
||||||
day: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
day: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
||||||
month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||||
|
|||||||
@ -5,6 +5,17 @@ Linkify =
|
|||||||
if Conf['Comment Expansion']
|
if Conf['Comment Expansion']
|
||||||
ExpandComment.callbacks.push @node
|
ExpandComment.callbacks.push @node
|
||||||
|
|
||||||
|
if Conf['Embedding'] or Conf['Link Title']
|
||||||
|
@embedProcess = Function 'link',
|
||||||
|
"var data = this.services(link);
|
||||||
|
if (data) {
|
||||||
|
#{
|
||||||
|
(if Conf['Embedding'] then 'this.embed(data);\n' else '') +
|
||||||
|
if Conf['Title Link'] then 'this.title(data);' else ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
|
||||||
Post.callbacks.push
|
Post.callbacks.push
|
||||||
name: 'Linkify'
|
name: 'Linkify'
|
||||||
cb: @node
|
cb: @node
|
||||||
@ -63,10 +74,7 @@ Linkify =
|
|||||||
Linkify.embedProcess Linkify.makeLink link, @
|
Linkify.embedProcess Linkify.makeLink link, @
|
||||||
return
|
return
|
||||||
|
|
||||||
embedProcess: (link) ->
|
embedProcess: -> return
|
||||||
if data = Linkify.services link
|
|
||||||
Linkify.embed data if Conf['Embedding']
|
|
||||||
Linkify.title data if Conf['Link Title']
|
|
||||||
|
|
||||||
regString: ///(
|
regString: ///(
|
||||||
# http, magnet, ftp, etc
|
# http, magnet, ftp, etc
|
||||||
|
|||||||
@ -8,14 +8,14 @@ Time =
|
|||||||
cb: @node
|
cb: @node
|
||||||
node: ->
|
node: ->
|
||||||
return if @isClone
|
return if @isClone
|
||||||
@nodes.date.textContent = Time.funk Time, @info.date
|
@nodes.date.textContent = Time.funk @info.date
|
||||||
createFunc: (format) ->
|
createFunc: (format) ->
|
||||||
code = format.replace /%([A-Za-z])/g, (s, c) ->
|
code = format.replace /%([A-Za-z])/g, (s, c) ->
|
||||||
if c of Time.formatters
|
if c of Time.formatters
|
||||||
"' + Time.formatters.#{c}.call(date) + '"
|
"' + this.formatters.#{c}.call(date) + '"
|
||||||
else
|
else
|
||||||
s
|
s
|
||||||
Function 'Time', 'date', "return '#{code}'"
|
Function 'date', "return '#{code}'"
|
||||||
day: [
|
day: [
|
||||||
'Sunday'
|
'Sunday'
|
||||||
'Monday'
|
'Monday'
|
||||||
|
|||||||
@ -1,19 +1,16 @@
|
|||||||
QR.captcha =
|
QR.captcha =
|
||||||
init: ->
|
init: ->
|
||||||
return if d.cookie.indexOf('pass_enabled=1') >= 0
|
return if d.cookie.indexOf('pass_enabled=1') >= 0
|
||||||
container = $.id 'captchaContainer'
|
return unless @isEnabled = !!$.id 'captchaContainer'
|
||||||
return unless @isEnabled = !!container
|
|
||||||
|
|
||||||
imgContainer = $.el 'div',
|
imgContainer = $.el 'div',
|
||||||
className: 'captcha-img'
|
className: 'captcha-img'
|
||||||
title: 'Reload reCAPTCHA'
|
title: 'Reload reCAPTCHA'
|
||||||
innerHTML: '<div><img></div>'
|
innerHTML: '<div><img></div>'
|
||||||
hidden: true
|
|
||||||
|
|
||||||
input = $.el 'input',
|
input = $.el 'input',
|
||||||
className: 'captcha-input field'
|
className: 'captcha-input field'
|
||||||
title: 'Verification'
|
title: 'Verification'
|
||||||
placeholder: 'Focus to load reCAPTCHA'
|
|
||||||
autocomplete: 'off'
|
autocomplete: 'off'
|
||||||
spellcheck: false
|
spellcheck: false
|
||||||
tabIndex: 45
|
tabIndex: 45
|
||||||
@ -22,20 +19,25 @@ QR.captcha =
|
|||||||
img: imgContainer.firstChild.firstChild
|
img: imgContainer.firstChild.firstChild
|
||||||
input: input
|
input: input
|
||||||
|
|
||||||
$.on input, 'focus', @setup
|
|
||||||
|
|
||||||
$.on input, 'blur', QR.focusout
|
$.on input, 'blur', QR.focusout
|
||||||
$.on input, 'focus', QR.focusin
|
$.on input, 'focus', QR.focusin
|
||||||
|
|
||||||
$.addClass QR.nodes.el, 'has-captcha'
|
$.addClass QR.nodes.el, 'has-captcha'
|
||||||
$.after QR.nodes.com.parentNode, [imgContainer, input]
|
$.after QR.nodes.com.parentNode, [imgContainer, input]
|
||||||
|
|
||||||
@setupObserver = new MutationObserver @afterSetup
|
@beforeSetup()
|
||||||
@setupObserver.observe container, childList: true
|
|
||||||
|
|
||||||
@setup() if Conf['Auto-load captcha']
|
|
||||||
@afterSetup() # reCAPTCHA might have loaded before the QR.
|
@afterSetup() # reCAPTCHA might have loaded before the QR.
|
||||||
|
|
||||||
|
beforeSetup: ->
|
||||||
|
{img, input} = @nodes
|
||||||
|
img.parentNode.parentNode.hidden = true
|
||||||
|
input.value = ''
|
||||||
|
input.placeholder = 'Focus to load reCAPTCHA'
|
||||||
|
$.on input, 'focus', @setup
|
||||||
|
@setupObserver = new MutationObserver @afterSetup
|
||||||
|
@setupObserver.observe $.id('captchaContainer'), childList: true
|
||||||
|
|
||||||
setup: ->
|
setup: ->
|
||||||
$.globalEval 'loadRecaptcha()'
|
$.globalEval 'loadRecaptcha()'
|
||||||
|
|
||||||
@ -44,21 +46,13 @@ QR.captcha =
|
|||||||
QR.captcha.setupObserver.disconnect()
|
QR.captcha.setupObserver.disconnect()
|
||||||
delete QR.captcha.setupObserver
|
delete QR.captcha.setupObserver
|
||||||
|
|
||||||
setLifetime = (e) -> 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
|
|
||||||
|
|
||||||
{img, input} = QR.captcha.nodes
|
{img, input} = QR.captcha.nodes
|
||||||
img.parentNode.parentNode.hidden = false
|
img.parentNode.parentNode.hidden = false
|
||||||
$.off input, 'focus', QR.captcha.setup
|
input.placeholder = 'Verification'
|
||||||
|
$.off input, 'focus', QR.captcha.setup
|
||||||
$.on input, 'keydown', QR.captcha.keydown.bind QR.captcha
|
$.on input, 'keydown', QR.captcha.keydown.bind QR.captcha
|
||||||
$.on img.parentNode, 'click', QR.captcha.reload.bind QR.captcha
|
$.on img.parentNode, 'click', QR.captcha.reload.bind QR.captcha
|
||||||
|
|
||||||
$.get 'captchas', [], ({captchas}) ->
|
|
||||||
QR.captcha.sync captchas
|
|
||||||
$.sync 'captchas', QR.captcha.sync
|
|
||||||
|
|
||||||
QR.captcha.nodes.challenge = challenge
|
QR.captcha.nodes.challenge = challenge
|
||||||
new MutationObserver(QR.captcha.load.bind QR.captcha).observe challenge,
|
new MutationObserver(QR.captcha.load.bind QR.captcha).observe challenge,
|
||||||
childList: true
|
childList: true
|
||||||
@ -66,68 +60,27 @@ QR.captcha =
|
|||||||
attributes: true
|
attributes: true
|
||||||
QR.captcha.load()
|
QR.captcha.load()
|
||||||
|
|
||||||
sync: (captchas) ->
|
destroy: ->
|
||||||
QR.captcha.captchas = captchas
|
$.globalEval 'Recaptcha.destroy()'
|
||||||
QR.captcha.count()
|
@beforeSetup()
|
||||||
|
|
||||||
getOne: ->
|
getOne: ->
|
||||||
@clear()
|
challenge = @nodes.img.alt
|
||||||
if captcha = @captchas.shift()
|
response = @nodes.input.value.trim()
|
||||||
{challenge, response} = captcha
|
if response and !/\s/.test response
|
||||||
@count()
|
|
||||||
$.set 'captchas', @captchas
|
|
||||||
else
|
|
||||||
challenge = @nodes.img.alt
|
|
||||||
if response = @nodes.input.value then @reload()
|
|
||||||
if response
|
|
||||||
response = response.trim()
|
|
||||||
# one-word-captcha:
|
# one-word-captcha:
|
||||||
# If there's only one word, duplicate it.
|
# If there's only one word, duplicate it.
|
||||||
response += " #{response}" unless /\s/.test response
|
response = "#{response} #{response}"
|
||||||
{challenge, response}
|
{challenge, response}
|
||||||
|
|
||||||
save: ->
|
|
||||||
return unless response = @nodes.input.value.trim()
|
|
||||||
@captchas.push
|
|
||||||
challenge: @nodes.img.alt
|
|
||||||
response: response
|
|
||||||
timeout: @timeout
|
|
||||||
@count()
|
|
||||||
@reload()
|
|
||||||
$.set 'captchas', @captchas
|
|
||||||
|
|
||||||
clear: ->
|
|
||||||
return unless @captchas # not loaded yet.
|
|
||||||
|
|
||||||
now = Date.now()
|
|
||||||
for captcha, i in @captchas
|
|
||||||
break if captcha.timeout > now
|
|
||||||
return unless i
|
|
||||||
@captchas = @captchas[i..]
|
|
||||||
@count()
|
|
||||||
$.set 'captchas', @captchas
|
|
||||||
|
|
||||||
load: ->
|
load: ->
|
||||||
return unless @nodes.challenge.firstChild
|
return unless @nodes.challenge.firstChild
|
||||||
# -1 minute to give upload some time.
|
# -1 minute to give upload some time.
|
||||||
@timeout = Date.now() + @lifetime * $.SECOND - $.MINUTE
|
|
||||||
challenge = @nodes.challenge.firstChild.value
|
challenge = @nodes.challenge.firstChild.value
|
||||||
@nodes.img.alt = challenge
|
@nodes.img.alt = challenge
|
||||||
@nodes.img.src = "//www.google.com/recaptcha/api/image?c=#{challenge}"
|
@nodes.img.src = "//www.google.com/recaptcha/api/image?c=#{challenge}"
|
||||||
@nodes.input.value = null
|
@nodes.input.value = null
|
||||||
@clear()
|
|
||||||
|
|
||||||
count: ->
|
|
||||||
count = if @captchas then @captchas.length else 0
|
|
||||||
@nodes.input.placeholder = switch count
|
|
||||||
when 0
|
|
||||||
'Verification (Shift + Enter to cache)'
|
|
||||||
when 1
|
|
||||||
'Verification (1 cached captcha)'
|
|
||||||
else
|
|
||||||
"Verification (#{count} cached captchas)"
|
|
||||||
|
|
||||||
@nodes.input.alt = count
|
|
||||||
|
|
||||||
reload: (focus) ->
|
reload: (focus) ->
|
||||||
# the 't' argument prevents the input from being focused
|
# the 't' argument prevents the input from being focused
|
||||||
@ -138,8 +91,6 @@ QR.captcha =
|
|||||||
keydown: (e) ->
|
keydown: (e) ->
|
||||||
if e.keyCode is 8 and not @nodes.input.value
|
if e.keyCode is 8 and not @nodes.input.value
|
||||||
@reload()
|
@reload()
|
||||||
else if e.keyCode is 13 and e.shiftKey
|
|
||||||
@save()
|
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|||||||
@ -701,6 +701,9 @@ QR =
|
|||||||
onerror: ->
|
onerror: ->
|
||||||
# Connection error, or www.4chan.org/banned
|
# Connection error, or www.4chan.org/banned
|
||||||
delete QR.req
|
delete QR.req
|
||||||
|
if QR.captcha.isEnabled
|
||||||
|
QR.captcha.destroy()
|
||||||
|
QR.captcha.setup()
|
||||||
post.unlock()
|
post.unlock()
|
||||||
QR.cooldown.auto = false
|
QR.cooldown.auto = false
|
||||||
QR.status()
|
QR.status()
|
||||||
@ -734,6 +737,7 @@ QR =
|
|||||||
{req} = QR
|
{req} = QR
|
||||||
delete QR.req
|
delete QR.req
|
||||||
|
|
||||||
|
QR.captcha.destroy() if QR.captcha.isEnabled
|
||||||
post = QR.posts[0]
|
post = QR.posts[0]
|
||||||
post.unlock()
|
post.unlock()
|
||||||
|
|
||||||
@ -763,25 +767,12 @@ QR =
|
|||||||
# Remove the obnoxious 4chan Pass ad.
|
# Remove the obnoxious 4chan Pass ad.
|
||||||
if /mistyped/i.test err.textContent
|
if /mistyped/i.test err.textContent
|
||||||
err = 'You seem to have mistyped the CAPTCHA.'
|
err = 'You seem to have mistyped the CAPTCHA.'
|
||||||
else if /expired/i.test err.textContent
|
QR.cooldown.auto = false
|
||||||
err = 'This CAPTCHA is no longer valid because it has expired.'
|
|
||||||
# Enable auto-post if we have some cached captchas.
|
|
||||||
QR.cooldown.auto = if QR.captcha.isEnabled
|
|
||||||
!!QR.captcha.captchas.length
|
|
||||||
else if err is 'Connection error with sys.4chan.org.'
|
|
||||||
true
|
|
||||||
else
|
|
||||||
# Something must've gone terribly wrong if you get captcha errors without captchas.
|
|
||||||
# Don't auto-post indefinitely in that case.
|
|
||||||
false
|
|
||||||
# Too many frequent mistyped captchas will auto-ban you!
|
# Too many frequent mistyped captchas will auto-ban you!
|
||||||
# On connection error, the post most likely didn't go through.
|
# On connection error, the post most likely didn't go through.
|
||||||
QR.cooldown.set delay: 2
|
QR.cooldown.set delay: 2
|
||||||
else if err.textContent and m = err.textContent.match /wait\s+(\d+)\s+second/i
|
else if err.textContent and m = err.textContent.match /wait\s+(\d+)\s+second/i
|
||||||
QR.cooldown.auto = if QR.captcha.isEnabled
|
QR.cooldown.auto = !QR.captcha.isEnabled
|
||||||
!!QR.captcha.captchas.length
|
|
||||||
else
|
|
||||||
true
|
|
||||||
QR.cooldown.set delay: m[1]
|
QR.cooldown.set delay: m[1]
|
||||||
else # stop auto-posting
|
else # stop auto-posting
|
||||||
QR.cooldown.auto = false
|
QR.cooldown.auto = false
|
||||||
@ -821,18 +812,6 @@ QR =
|
|||||||
# Enable auto-posting if we have stuff left to post, disable it otherwise.
|
# Enable auto-posting if we have stuff left to post, disable it otherwise.
|
||||||
postsCount = QR.posts.length - 1
|
postsCount = QR.posts.length - 1
|
||||||
QR.cooldown.auto = postsCount and isReply
|
QR.cooldown.auto = postsCount and isReply
|
||||||
if QR.cooldown.auto and QR.captcha.isEnabled and (captchasCount = QR.captcha.captchas.length) < 3 and captchasCount < postsCount
|
|
||||||
notif = new Notification 'Quick reply warning',
|
|
||||||
body: "You are running low on cached captchas. Cache count: #{captchasCount}."
|
|
||||||
icon: Favicon.logo
|
|
||||||
notif.onclick = ->
|
|
||||||
QR.open()
|
|
||||||
QR.captcha.nodes.input.focus()
|
|
||||||
window.focus()
|
|
||||||
notif.onshow = ->
|
|
||||||
setTimeout ->
|
|
||||||
notif.close()
|
|
||||||
, 7 * $.SECOND
|
|
||||||
|
|
||||||
unless Conf['Persistent QR'] or QR.cooldown.auto
|
unless Conf['Persistent QR'] or QR.cooldown.auto
|
||||||
QR.close()
|
QR.close()
|
||||||
|
|||||||
@ -91,6 +91,8 @@ QR.post = class
|
|||||||
return unless @ is QR.selected
|
return unless @ is QR.selected
|
||||||
for name in ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag'] when node = QR.nodes[name]
|
for name in ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag'] when node = QR.nodes[name]
|
||||||
node.disabled = lock
|
node.disabled = lock
|
||||||
|
if QR.captcha.isEnabled
|
||||||
|
QR.captcha.nodes.input.disabled = lock
|
||||||
@nodes.rm.style.visibility = if lock then 'hidden' else ''
|
@nodes.rm.style.visibility = if lock then 'hidden' else ''
|
||||||
(if lock then $.off else $.on) QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput
|
(if lock then $.off else $.on) QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput
|
||||||
@nodes.spoiler.disabled = lock
|
@nodes.spoiler.disabled = lock
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user