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;', "
\uf00d
+
No selected fileSpoiler+Dump\uf00dRemove File
") }; @@ -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