diff --git a/LICENSE b/LICENSE
index 8a440e0f0..a2e3b06f9 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
/*
-* appchan x - Version 2.8.8 - 2014-01-30
+* appchan x - Version 2.8.8 - 2014-02-09
*
* Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
diff --git a/builds/appchan-x.user.js b/builds/appchan-x.user.js
index 72e21136c..f546ff29f 100644
--- a/builds/appchan-x.user.js
+++ b/builds/appchan-x.user.js
@@ -23,7 +23,7 @@
// ==/UserScript==
/*
-* appchan x - Version 2.8.8 - 2014-01-30
+* appchan x - Version 2.8.8 - 2014-02-09
*
* Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
@@ -8327,15 +8327,10 @@
return QR.status();
},
focusin: function() {
- return $.addClass(QR.nodes.el, 'has-focus');
+ return $.addClass(QR.nodes.el, 'focus');
},
focusout: function() {
- return $.queueTask(function() {
- if ($.x('ancestor::div[@id="qr"]', d.activeElement)) {
- return;
- }
- return $.rmClass(QR.nodes.el, 'has-focus');
- });
+ return $.rmClass(QR.nodes.el, 'focus');
},
hide: function() {
d.activeElement.blur();
@@ -8665,8 +8660,6 @@
$.on(elm, 'blur', QR.focusout);
$.on(elm, 'focus', QR.focusin);
}
- $.on(dialog, 'focusin', QR.focusin);
- $.on(dialog, 'focusout', QR.focusout);
$.on(nodes.autohide, 'change', QR.toggleHide);
$.on(nodes.close, 'click', QR.close);
$.on(nodes.dumpButton, 'click', function() {
@@ -9035,64 +9028,74 @@
QR.captcha = {
init: function() {
+ var container, imgContainer, input;
if (d.cookie.indexOf('pass_enabled=1') >= 0) {
return;
}
- if (!(this.isEnabled = !!$.id('captchaFormPart'))) {
+ container = $.id('captchaContainer');
+ if (!(this.isEnabled = !!container)) {
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', {
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
+ spellcheck: false
});
this.nodes = {
- challenge: $.id('recaptcha_challenge_field_holder'),
- img: $('img', imgContainer),
+ img: imgContainer.firstChild,
input: input
};
- new MutationObserver(this.load.bind(this)).observe(this.nodes.challenge, {
- 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, 'focus', this.setup);
$.on(input, 'blur', QR.focusout);
$.on(input, 'focus', QR.focusin);
$.addClass(QR.nodes.el, 'has-captcha');
- return $.after(QR.nodes.com.parentElement, [imgContainer, input]);
+ $.after(QR.nodes.com.parentNode, [imgContainer, input]);
+ this.setupObserver = new MutationObserver(this.afterSetup);
+ this.setupObserver.observe(container, {
+ childList: true
+ });
+ return this.afterSetup();
+ },
+ 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) {
QR.captcha.captchas = captchas;
diff --git a/builds/crx/script.js b/builds/crx/script.js
index f89bc5dd6..73ec1e200 100644
--- a/builds/crx/script.js
+++ b/builds/crx/script.js
@@ -1,6 +1,6 @@
// Generated by CoffeeScript
/*
-* appchan x - Version 2.8.8 - 2014-01-30
+* appchan x - Version 2.8.8 - 2014-02-09
*
* Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
@@ -8363,10 +8363,10 @@
return QR.status();
},
focusin: function() {
- return $.addClass(QR.nodes.el, 'has-focus');
+ return $.addClass(QR.nodes.el, 'focus');
},
focusout: function() {
- return $.rmClass(QR.nodes.el, 'has-focus');
+ return $.rmClass(QR.nodes.el, 'focus');
},
hide: function() {
d.activeElement.blur();
@@ -8635,7 +8635,7 @@
}
},
dialog: function() {
- var check, dialog, event, i, items, key, mimeTypes, name, node, nodes, save, value, _ref;
+ var check, dialog, elm, event, i, items, key, mimeTypes, name, node, nodes, save, value, _ref;
QR.nodes = nodes = {
el: dialog = UI.dialog('qr', 'top:0;right:0;', "")
};
@@ -8699,8 +8699,12 @@
}
QR.flagsInput();
$.on(nodes.filename.parentNode, 'click keydown', QR.openFileInput);
- $.on(dialog, 'focusin', QR.focusin);
- $.on(dialog, 'focusout', QR.focusout);
+ items = $$('*', QR.nodes.el);
+ i = 0;
+ while (elm = items[i++]) {
+ $.on(elm, 'blur', QR.focusout);
+ $.on(elm, 'focus', QR.focusin);
+ }
$.on(nodes.autohide, 'change', QR.toggleHide);
$.on(nodes.close, 'click', QR.close);
$.on(nodes.dumpButton, 'click', function() {
@@ -9058,62 +9062,74 @@
QR.captcha = {
init: function() {
+ var container, imgContainer, input;
if (d.cookie.indexOf('pass_enabled=1') >= 0) {
return;
}
- if (!(this.isEnabled = !!$.id('captchaFormPart'))) {
+ container = $.id('captchaContainer');
+ if (!(this.isEnabled = !!container)) {
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', {
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
+ spellcheck: false
});
this.nodes = {
- challenge: $.id('recaptcha_challenge_field_holder'),
- img: $('img', imgContainer),
+ img: imgContainer.firstChild,
input: input
};
- new MutationObserver(this.load.bind(this)).observe(this.nodes.challenge, {
+ $.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.setupObserver = new MutationObserver(this.afterSetup);
+ this.setupObserver.observe(container, {
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');
- });
+ return this.afterSetup();
+ },
+ 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 _this.sync(captchas);
+ return QR.captcha.sync(captchas);
});
- $.sync('captchas', this.sync);
- this.reload();
- $.addClass(QR.nodes.el, 'has-captcha');
- return $.after(QR.nodes.com.parentElement, [imgContainer, input]);
+ $.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) {
QR.captcha.captchas = captchas;
diff --git a/src/Posting/QR.captcha.coffee b/src/Posting/QR.captcha.coffee
index 7055c677d..24ad74f48 100644
--- a/src/Posting/QR.captcha.coffee
+++ b/src/Posting/QR.captcha.coffee
@@ -1,53 +1,63 @@
QR.captcha =
init: ->
return if d.cookie.indexOf('pass_enabled=1') >= 0
- return unless @isEnabled = !!$.id 'captchaFormPart'
- $.asap (-> $.id 'recaptcha_challenge_field_holder'), @ready.bind @
-
- 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
+ container = $.id 'captchaContainer'
+ return unless @isEnabled = !!container
imgContainer = $.el 'div',
className: 'captcha-img'
title: 'Reload reCAPTCHA'
- innerHTML: ''
+ innerHTML: '
'
+ hidden: true
input = $.el 'input',
- className: 'captcha-input field'
- title: 'Verification'
+ className: 'captcha-input field'
+ title: 'Verification'
+ placeholder: 'Focus to load reCAPTCHA'
autocomplete: 'off'
- spellcheck: false
- tabIndex: 45
+ spellcheck: false
@nodes =
- challenge: $.id 'recaptcha_challenge_field_holder'
- img: $ 'img', imgContainer
- input: input
+ img: imgContainer.firstChild
+ input: input
- new MutationObserver(@load.bind @).observe @nodes.challenge,
- childList: true
+ $.on input, 'focus', @setup
- $.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') { %>
- # XXX Firefox lacks focusin/focusout support.
$.on input, 'blur', QR.focusout
$.on input, 'focus', QR.focusin
- <% } %>
$.addClass QR.nodes.el, 'has-captcha'
- $.after QR.nodes.com.parentElement, [imgContainer, input]
+ $.after QR.nodes.com.parentNode, [imgContainer, input]
+
+ @setupObserver = new MutationObserver @afterSetup
+ @setupObserver.observe container, childList: true
+ @afterSetup() # reCAPTCHA might have loaded before the QR.
+
+ 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) ->
QR.captcha.captchas = captchas
diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee
index d468d2f87..7f4ddc753 100644
--- a/src/Posting/QR.coffee
+++ b/src/Posting/QR.coffee
@@ -113,16 +113,11 @@ QR =
QR.status()
focusin: ->
- $.addClass QR.nodes.el, 'has-focus'
+ $.addClass QR.nodes.el, 'focus'
focusout: ->
- <% if (type === 'crx') { %>
- $.rmClass QR.nodes.el, 'has-focus'
- <% } else { %>
- $.queueTask ->
- return if $.x 'ancestor::div[@id="qr"]', d.activeElement
- $.rmClass QR.nodes.el, 'has-focus'
- <% } %>
+ $.rmClass QR.nodes.el, 'focus'
+
hide: ->
d.activeElement.blur()
$.addClass QR.nodes.el, 'autohide'
@@ -415,17 +410,11 @@ QR =
$.on nodes.filename.parentNode, 'click keydown', QR.openFileInput
- <% if (type === 'userscript') { %>
- # XXX Firefox lacks focusin/focusout support.
items = $$ '*', QR.nodes.el
i = 0
while elm = items[i++]
$.on elm, 'blur', QR.focusout
$.on elm, 'focus', QR.focusin
- <% } %>
-
- $.on dialog, 'focusin', QR.focusin
- $.on dialog, 'focusout', QR.focusout
$.on nodes.autohide, 'change', QR.toggleHide
$.on nodes.close, 'click', QR.close