Copy Mayhem

This commit is contained in:
Kabir Sala 2014-02-09 19:36:28 +01:00
parent 8bb2ac71bd
commit 3bd506e617
8 changed files with 140 additions and 112 deletions

View File

@ -1,5 +1,5 @@
/* /*
* 4chan X - Version 1.3.2 - 2014-01-12 * 4chan X - Version 1.3.3 - 2014-02-09
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE

View File

@ -1,6 +1,6 @@
// ==UserScript== // ==UserScript==
// @name 4chan X // @name 4chan X
// @version 1.3.2 // @version 1.3.3
// @minGMVer 1.13 // @minGMVer 1.13
// @minFFVer 26 // @minFFVer 26
// @namespace 4chan-X // @namespace 4chan-X

View File

@ -1,7 +1,7 @@
// Generated by CoffeeScript // Generated by CoffeeScript
// ==UserScript== // ==UserScript==
// @name 4chan X // @name 4chan X
// @version 1.3.2 // @version 1.3.3
// @minGMVer 1.13 // @minGMVer 1.13
// @minFFVer 26 // @minFFVer 26
// @namespace 4chan-X // @namespace 4chan-X
@ -22,7 +22,7 @@
// ==/UserScript== // ==/UserScript==
/* /*
* 4chan X - Version 1.3.2 - 2014-01-12 * 4chan X - Version 1.3.3 - 2014-02-09
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
@ -358,7 +358,7 @@
doc = d.documentElement; doc = d.documentElement;
g = { g = {
VERSION: '1.3.2', VERSION: '1.3.3',
NAMESPACE: '4chan X.', NAMESPACE: '4chan X.',
boards: {}, boards: {},
threads: {}, threads: {},
@ -6113,64 +6113,73 @@
QR.captcha = { QR.captcha = {
init: function() { init: function() {
var container, imgContainer, input;
if (d.cookie.indexOf('pass_enabled=1') >= 0) { if (d.cookie.indexOf('pass_enabled=1') >= 0) {
return; return;
} }
if (!(this.isEnabled = !!$.id('captchaFormPart'))) { container = $.id('captchaContainer');
if (!(this.isEnabled = !!container)) {
return; return;
} }
return $.asap((function() {
return $.id('recaptcha_challenge_field_holder');
}), this.ready.bind(this));
},
ready: function() {
var imgContainer, input, setLifetime,
_this = this;
setLifetime = function(e) {
return _this.lifetime = e.detail;
};
$.on(window, 'captcha:timeout', setLifetime);
$.globalEval('window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))');
$.off(window, 'captcha:timeout', setLifetime);
imgContainer = $.el('div', { imgContainer = $.el('div', {
className: 'captcha-img', className: 'captcha-img',
title: 'Reload reCAPTCHA', title: 'Reload reCAPTCHA',
innerHTML: '<img>' innerHTML: '<img>',
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: 55
}); });
this.nodes = { this.nodes = {
challenge: $.id('recaptcha_challenge_field_holder'),
img: imgContainer.firstChild, img: imgContainer.firstChild,
input: input input: input
}; };
new MutationObserver(this.load.bind(this)).observe(this.nodes.challenge, { $.on(input, 'focus', this.setup);
childList: true
});
$.on(imgContainer, 'click', this.reload.bind(this));
$.on(input, 'keydown', this.keydown.bind(this));
$.on(input, 'focus', function() {
return $.addClass(QR.nodes.el, 'focus');
});
$.on(input, 'blur', function() {
return $.rmClass(QR.nodes.el, 'focus');
});
$.get('captchas', [], function(_arg) {
var captchas;
captchas = _arg.captchas;
return _this.sync(captchas);
});
$.sync('captchas', this.sync);
this.reload();
$.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');
return $.after(QR.nodes.com.parentNode, [imgContainer, input]); $.after(QR.nodes.com.parentNode, [imgContainer, input]);
this.setupObserver = new MutationObserver(this.afterSetup);
return this.setupObserver.observe(container, {
childList: true
});
},
setup: function() {
return $.globalEval('loadRecaptcha()');
},
afterSetup: function() {
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;
$.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
});
return QR.captcha.load();
}, },
sync: function(captchas) { sync: function(captchas) {
QR.captcha.captchas = captchas; QR.captcha.captchas = captchas;
@ -6216,6 +6225,9 @@
}, },
clear: function() { clear: function() {
var captcha, i, now, _i, _len, _ref; var captcha, i, now, _i, _len, _ref;
if (!this.captchas.length) {
return;
}
now = Date.now(); now = Date.now();
_ref = this.captchas; _ref = this.captchas;
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {

View File

@ -1,6 +1,6 @@
{ {
"name": "4chan X", "name": "4chan X",
"version": "1.3.2", "version": "1.3.3",
"manifest_version": 2, "manifest_version": 2,
"description": "Cross-browser userscript for maximum lurking on 4chan.", "description": "Cross-browser userscript for maximum lurking on 4chan.",
"icons": { "icons": {

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript // Generated by CoffeeScript
/* /*
* 4chan X - Version 1.3.2 - 2014-01-12 * 4chan X - Version 1.3.3 - 2014-02-09
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
@ -336,7 +336,7 @@
doc = d.documentElement; doc = d.documentElement;
g = { g = {
VERSION: '1.3.2', VERSION: '1.3.3',
NAMESPACE: '4chan X.', NAMESPACE: '4chan X.',
boards: {}, boards: {},
threads: {}, threads: {},
@ -6104,62 +6104,71 @@
QR.captcha = { QR.captcha = {
init: function() { init: function() {
var container, imgContainer, input;
if (d.cookie.indexOf('pass_enabled=1') >= 0) { if (d.cookie.indexOf('pass_enabled=1') >= 0) {
return; return;
} }
if (!(this.isEnabled = !!$.id('captchaFormPart'))) { container = $.id('captchaContainer');
if (!(this.isEnabled = !!container)) {
return; return;
} }
return $.asap((function() {
return $.id('recaptcha_challenge_field_holder');
}), this.ready.bind(this));
},
ready: function() {
var imgContainer, input, setLifetime,
_this = this;
setLifetime = function(e) {
return _this.lifetime = e.detail;
};
$.on(window, 'captcha:timeout', setLifetime);
$.globalEval('window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))');
$.off(window, 'captcha:timeout', setLifetime);
imgContainer = $.el('div', { imgContainer = $.el('div', {
className: 'captcha-img', className: 'captcha-img',
title: 'Reload reCAPTCHA', title: 'Reload reCAPTCHA',
innerHTML: '<img>' innerHTML: '<img>',
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: 55
}); });
this.nodes = { this.nodes = {
challenge: $.id('recaptcha_challenge_field_holder'),
img: imgContainer.firstChild, img: imgContainer.firstChild,
input: input input: input
}; };
new MutationObserver(this.load.bind(this)).observe(this.nodes.challenge, { $.on(input, 'focus', this.setup);
$.addClass(QR.nodes.el, 'has-captcha');
$.after(QR.nodes.com.parentNode, [imgContainer, input]);
this.setupObserver = new MutationObserver(this.afterSetup);
return this.setupObserver.observe(container, {
childList: true childList: true
}); });
$.on(imgContainer, 'click', this.reload.bind(this)); },
$.on(input, 'keydown', this.keydown.bind(this)); setup: function() {
$.on(input, 'focus', function() { return $.globalEval('loadRecaptcha()');
return $.addClass(QR.nodes.el, 'focus'); },
}); afterSetup: function() {
$.on(input, 'blur', function() { var challenge, img, input, setLifetime, _ref;
return $.rmClass(QR.nodes.el, 'focus'); 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;
$.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) { $.get('captchas', [], function(_arg) {
var captchas; var captchas;
captchas = _arg.captchas; captchas = _arg.captchas;
return _this.sync(captchas); return QR.captcha.sync(captchas);
}); });
$.sync('captchas', this.sync); $.sync('captchas', QR.captcha.sync);
this.reload(); QR.captcha.nodes.challenge = challenge;
$.addClass(QR.nodes.el, 'has-captcha'); new MutationObserver(QR.captcha.load.bind(QR.captcha)).observe(challenge, {
return $.after(QR.nodes.com.parentNode, [imgContainer, input]); childList: true
});
return QR.captcha.load();
}, },
sync: function(captchas) { sync: function(captchas) {
QR.captcha.captchas = captchas; QR.captcha.captchas = captchas;
@ -6205,6 +6214,9 @@
}, },
clear: function() { clear: function() {
var captcha, i, now, _i, _len, _ref; var captcha, i, now, _i, _len, _ref;
if (!this.captchas.length) {
return;
}
now = Date.now(); now = Date.now();
_ref = this.captchas; _ref = this.captchas;
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {

View File

@ -1 +1 @@
postMessage({version:'1.3.2'},'*') postMessage({version:'1.3.3'},'*')

View File

@ -1,6 +1,6 @@
{ {
"name": "4chan-X", "name": "4chan-X",
"version": "1.3.2", "version": "1.3.3",
"description": "Cross-browser userscript for maximum lurking on 4chan.", "description": "Cross-browser userscript for maximum lurking on 4chan.",
"meta": { "meta": {
"name": "4chan X", "name": "4chan X",

View File

@ -1,43 +1,25 @@
QR.captcha = QR.captcha =
init: -> init: ->
return if d.cookie.indexOf('pass_enabled=1') >= 0 return if d.cookie.indexOf('pass_enabled=1') >= 0
return unless @isEnabled = !!$.id 'captchaFormPart' container = $.id 'captchaContainer'
$.asap (-> $.id 'recaptcha_challenge_field_holder'), @ready.bind @ return unless @isEnabled = !!container
ready: ->
setLifetime = (e) => @lifetime = e.detail
$.on window, 'captcha:timeout', setLifetime
$.globalEval 'window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))'
$.off window, 'captcha:timeout', setLifetime
imgContainer = $.el 'div', imgContainer = $.el 'div',
className: 'captcha-img' className: 'captcha-img'
title: 'Reload reCAPTCHA' title: 'Reload reCAPTCHA'
innerHTML: '<img>' innerHTML: '<img>'
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: 55
@nodes = @nodes =
challenge: $.id 'recaptcha_challenge_field_holder' img: imgContainer.firstChild
img: imgContainer.firstChild input: input
input: input
new MutationObserver(@load.bind @).observe @nodes.challenge, $.on input, 'focus', @setup
childList: true
$.on imgContainer, 'click', @reload.bind @
$.on input, 'keydown', @keydown.bind @
$.on input, 'focus', -> $.addClass QR.nodes.el, 'focus'
$.on input, 'blur', -> $.rmClass QR.nodes.el, 'focus'
$.get 'captchas', [], ({captchas}) =>
@sync captchas
$.sync 'captchas', @sync
# start with an uncached captcha
@reload()
<% if (type === 'userscript') { %> <% if (type === 'userscript') { %>
# XXX Firefox lacks focusin/focusout support. # XXX Firefox lacks focusin/focusout support.
@ -48,10 +30,37 @@ QR.captcha =
$.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
@setupObserver.observe container, childList: true
setup: ->
$.globalEval 'loadRecaptcha()'
afterSetup: ->
return unless challenge = $.id 'recaptcha_challenge_field_holder'
QR.captcha.setupObserver.disconnect()
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.parentNode.hidden = false
$.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', [], ({captchas}) ->
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
QR.captcha.load()
sync: (captchas) -> sync: (captchas) ->
QR.captcha.captchas = captchas QR.captcha.captchas = captchas
QR.captcha.count() QR.captcha.count()
getOne: -> getOne: ->
@clear() @clear()
if captcha = @captchas.shift() if captcha = @captchas.shift()
@ -67,7 +76,6 @@ QR.captcha =
# If there's only one word, duplicate it. # If there's only one word, duplicate it.
response = "#{response} #{response}" unless /\s/.test response response = "#{response} #{response}" unless /\s/.test response
{challenge, response} {challenge, response}
save: -> save: ->
return unless response = @nodes.input.value.trim() return unless response = @nodes.input.value.trim()
@captchas.push @captchas.push
@ -77,8 +85,8 @@ QR.captcha =
@count() @count()
@reload() @reload()
$.set 'captchas', @captchas $.set 'captchas', @captchas
clear: -> clear: ->
return unless @captchas.length
now = Date.now() now = Date.now()
for captcha, i in @captchas for captcha, i in @captchas
break if captcha.timeout > now break if captcha.timeout > now
@ -86,7 +94,6 @@ QR.captcha =
@captchas = @captchas[i..] @captchas = @captchas[i..]
@count() @count()
$.set 'captchas', @captchas $.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.
@ -96,7 +103,6 @@ QR.captcha =
@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() @clear()
count: -> count: ->
count = @captchas.length count = @captchas.length
@nodes.input.placeholder = switch count @nodes.input.placeholder = switch count
@ -107,13 +113,11 @@ QR.captcha =
else else
"Verification (#{count} cached captchas)" "Verification (#{count} cached captchas)"
@nodes.input.alt = count # For XTRM RICE. @nodes.input.alt = count # For XTRM RICE.
reload: (focus) -> reload: (focus) ->
# the 't' argument prevents the input from being focused # the 't' argument prevents the input from being focused
$.globalEval 'Recaptcha.reload("t")' $.globalEval 'Recaptcha.reload("t")'
# Focus if we meant to. # Focus if we meant to.
@nodes.input.focus() if focus @nodes.input.focus() if focus
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()
@ -121,4 +125,4 @@ QR.captcha =
@save() @save()
else else
return return
e.preventDefault() e.preventDefault()