Use noscript fallback for captcha.
This commit is contained in:
parent
27f980db61
commit
08050f33f1
@ -10,6 +10,7 @@
|
|||||||
"oldVersions": "https://raw.githubusercontent.com/ccd0/4chan-x/",
|
"oldVersions": "https://raw.githubusercontent.com/ccd0/4chan-x/",
|
||||||
"faq": "https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions",
|
"faq": "https://github.com/ccd0/4chan-x/wiki/Frequently-Asked-Questions",
|
||||||
"appid": "lacclbnghgdicfifcamcmcnilckjamag",
|
"appid": "lacclbnghgdicfifcamcmcnilckjamag",
|
||||||
|
"recaptchaKey": "6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc",
|
||||||
"youtubeAPIKey": "AIzaSyB5_zaen_-46Uhz1xGR-lz1YoUMHqCD6CE",
|
"youtubeAPIKey": "AIzaSyB5_zaen_-46Uhz1xGR-lz1YoUMHqCD6CE",
|
||||||
"buildsPath": "builds/",
|
"buildsPath": "builds/",
|
||||||
"mainBranch": "master",
|
"mainBranch": "master",
|
||||||
@ -17,7 +18,8 @@
|
|||||||
"*://boards.4chan.org/*",
|
"*://boards.4chan.org/*",
|
||||||
"*://sys.4chan.org/*",
|
"*://sys.4chan.org/*",
|
||||||
"*://a.4cdn.org/*",
|
"*://a.4cdn.org/*",
|
||||||
"*://i.4cdn.org/*"
|
"*://i.4cdn.org/*",
|
||||||
|
"*://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc"
|
||||||
],
|
],
|
||||||
"suffix": {
|
"suffix": {
|
||||||
"stable": "",
|
"stable": "",
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
Main =
|
Main =
|
||||||
init: ->
|
init: ->
|
||||||
|
if location.hostname is 'www.google.com'
|
||||||
|
return $.ready -> QR.captcha.initFrame()
|
||||||
|
|
||||||
g.threads = new SimpleDict
|
g.threads = new SimpleDict
|
||||||
g.posts = new SimpleDict
|
g.posts = new SimpleDict
|
||||||
|
|
||||||
|
|||||||
@ -1074,6 +1074,9 @@ input.field.tripped:not(:hover):not(:focus) {
|
|||||||
.captcha-input.error:focus {
|
.captcha-input.error:focus {
|
||||||
border-color: rgb(255,0,0) !important;
|
border-color: rgb(255,0,0) !important;
|
||||||
}
|
}
|
||||||
|
#qr-captcha-iframe {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.field {
|
.field {
|
||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
|||||||
@ -9,3 +9,4 @@
|
|||||||
<%= grunt.file.read('src/General/lib/randomaccesslist.class') %>
|
<%= grunt.file.read('src/General/lib/randomaccesslist.class') %>
|
||||||
<%= grunt.file.read('src/General/lib/simpledict.class') %>
|
<%= grunt.file.read('src/General/lib/simpledict.class') %>
|
||||||
<%= grunt.file.read('src/General/lib/set.class') %>
|
<%= grunt.file.read('src/General/lib/set.class') %>
|
||||||
|
<%= grunt.file.read('src/General/lib/connection.class') %>
|
||||||
|
|||||||
16
src/General/lib/connection.class
Normal file
16
src/General/lib/connection.class
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
class Connection
|
||||||
|
constructor: (@target, @origin, @cb) ->
|
||||||
|
$.on window, 'message', @onMessage.bind @
|
||||||
|
|
||||||
|
send: (data) ->
|
||||||
|
@target.postMessage "#{g.NAMESPACE}#{JSON.stringify data}", @origin
|
||||||
|
|
||||||
|
onMessage: (e) ->
|
||||||
|
return unless e.source is @target and
|
||||||
|
e.origin is @origin and
|
||||||
|
typeof e.data is 'string' and
|
||||||
|
e.data[...g.NAMESPACE.length] is g.NAMESPACE
|
||||||
|
data = JSON.parse e.data[g.NAMESPACE.length..]
|
||||||
|
for type, value of data
|
||||||
|
@cb[type]? value
|
||||||
|
return
|
||||||
@ -1,31 +1,34 @@
|
|||||||
QR.captcha =
|
QR.captcha =
|
||||||
|
lifetime: 120 * $.SECOND
|
||||||
|
iframeURL: '//www.google.com/recaptcha/api/fallback?k=<%= meta.recaptchaKey %>'
|
||||||
|
|
||||||
init: ->
|
init: ->
|
||||||
return if d.cookie.indexOf('pass_enabled=1') >= 0
|
return if d.cookie.indexOf('pass_enabled=1') >= 0
|
||||||
return unless @isEnabled = !!$.id 'captchaContainer'
|
return unless @isEnabled = !!$.id 'g-recaptcha'
|
||||||
|
|
||||||
$.globalEval 'loadRecaptcha()' if Conf['Auto-load captcha']
|
container = $.el 'div',
|
||||||
|
|
||||||
imgContainer = $.el 'div',
|
|
||||||
className: 'captcha-img'
|
className: 'captcha-img'
|
||||||
title: 'Reload reCAPTCHA'
|
title: 'Reload reCAPTCHA'
|
||||||
$.extend imgContainer, <%= html('<img>') %>
|
|
||||||
input = $.el 'input',
|
input = $.el 'input',
|
||||||
className: 'captcha-input field'
|
className: 'captcha-input field'
|
||||||
title: 'Verification'
|
title: 'Verification'
|
||||||
autocomplete: 'off'
|
autocomplete: 'off'
|
||||||
spellcheck: false
|
spellcheck: false
|
||||||
tabIndex: 45
|
tabIndex: 45
|
||||||
@nodes =
|
@nodes = {container, input}
|
||||||
img: imgContainer.firstChild
|
|
||||||
input: input
|
|
||||||
|
|
||||||
$.on input, 'blur', QR.focusout
|
$.on input, 'blur', QR.focusout
|
||||||
$.on input, 'focus', QR.focusin
|
$.on input, 'focus', QR.focusin
|
||||||
$.on input, 'keydown', QR.captcha.keydown.bind QR.captcha
|
$.on input, 'keydown', @keydown.bind @
|
||||||
$.on @nodes.img.parentNode, 'click', QR.captcha.reload.bind QR.captcha
|
$.on @nodes.container, 'click', @reload.bind(@, true)
|
||||||
|
|
||||||
|
@conn = new Connection null, "#{location.protocol}//www.google.com",
|
||||||
|
challenge: @load.bind @
|
||||||
|
token: @save.bind @
|
||||||
|
error: @error.bind @
|
||||||
|
|
||||||
$.addClass QR.nodes.el, 'has-captcha'
|
$.addClass QR.nodes.el, 'has-captcha'
|
||||||
$.after QR.nodes.com.parentNode, [imgContainer, input]
|
$.after QR.nodes.com.parentNode, [container, input]
|
||||||
|
|
||||||
@captchas = []
|
@captchas = []
|
||||||
$.get 'captchas', [], ({captchas}) ->
|
$.get 'captchas', [], ({captchas}) ->
|
||||||
@ -33,77 +36,112 @@ QR.captcha =
|
|||||||
QR.captcha.clear()
|
QR.captcha.clear()
|
||||||
$.sync 'captchas', @sync
|
$.sync 'captchas', @sync
|
||||||
|
|
||||||
new MutationObserver(@afterSetup).observe $.id('captchaContainer'), childList: true
|
|
||||||
|
|
||||||
@beforeSetup()
|
@beforeSetup()
|
||||||
@afterSetup() # reCAPTCHA might have loaded before the QR.
|
@setup() if Conf['Auto-load captcha']
|
||||||
|
|
||||||
|
initFrame: ->
|
||||||
|
conn = new Connection window.top, "#{location.protocol}//boards.4chan.org",
|
||||||
|
queryChallenge: ->
|
||||||
|
conn.send {challenge}
|
||||||
|
response: (response) ->
|
||||||
|
$.id('response').value = response
|
||||||
|
$('.fbc-challenge > form').submit()
|
||||||
|
challenge = $('.fbc-payload > img')?.src
|
||||||
|
token = $('.fbc-verification-token > textarea')?.value
|
||||||
|
error = $('.fbc-error')?.textContent
|
||||||
|
conn.send {challenge, token, error}
|
||||||
|
|
||||||
|
cb:
|
||||||
|
focus: -> QR.captcha.setup()
|
||||||
|
|
||||||
beforeSetup: ->
|
beforeSetup: ->
|
||||||
{img, input} = @nodes
|
{container, input} = @nodes
|
||||||
img.parentNode.hidden = true
|
container.hidden = true
|
||||||
input.value = ''
|
input.value = ''
|
||||||
input.placeholder = 'Focus to load reCAPTCHA'
|
input.placeholder = 'Focus to load reCAPTCHA'
|
||||||
@count()
|
@count()
|
||||||
$.on input, 'focus', @setup
|
$.on input, 'focus', @cb.focus
|
||||||
|
|
||||||
setup: ->
|
setup: ->
|
||||||
$.globalEval 'loadRecaptcha()'
|
if !@nodes.iframe
|
||||||
|
@nodes.iframe = $.el 'iframe',
|
||||||
|
id: 'qr-captcha-iframe'
|
||||||
|
src: @iframeURL
|
||||||
|
delete @iframeUsed
|
||||||
|
$.add d.body, @nodes.iframe
|
||||||
|
@conn.target = @nodes.iframe.contentWindow
|
||||||
|
else if @iframeUsed or !@nodes.img
|
||||||
|
@nodes.iframe.src = @iframeURL
|
||||||
|
delete @iframeUsed
|
||||||
|
else if !@nodes.img.complete
|
||||||
|
@conn.send queryChallenge: null
|
||||||
|
|
||||||
afterSetup: ->
|
afterSetup: ->
|
||||||
return unless challenge = $.id 'recaptcha_challenge_field_holder'
|
{container, input} = @nodes
|
||||||
return if challenge is QR.captcha.nodes.challenge
|
container.hidden = false
|
||||||
|
|
||||||
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
|
|
||||||
input.placeholder = 'Verification'
|
input.placeholder = 'Verification'
|
||||||
QR.captcha.count()
|
@count()
|
||||||
$.off input, 'focus', QR.captcha.setup
|
$.off input, 'focus', @cb.focus
|
||||||
|
|
||||||
QR.captcha.nodes.challenge = challenge
|
|
||||||
new MutationObserver(QR.captcha.load.bind QR.captcha).observe challenge,
|
|
||||||
childList: true
|
|
||||||
subtree: true
|
|
||||||
attributes: true
|
|
||||||
QR.captcha.load()
|
|
||||||
|
|
||||||
if QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight
|
if QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight
|
||||||
QR.nodes.el.style.top = null
|
QR.nodes.el.style.top = null
|
||||||
QR.nodes.el.style.bottom = '0px'
|
QR.nodes.el.style.bottom = '0px'
|
||||||
|
|
||||||
destroy: ->
|
destroy: ->
|
||||||
$.globalEval 'Recaptcha.destroy()'
|
$.rm @nodes.img
|
||||||
|
delete @nodes.img
|
||||||
|
$.rm @nodes.iframe
|
||||||
|
delete @nodes.iframe
|
||||||
@beforeSetup()
|
@beforeSetup()
|
||||||
|
|
||||||
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()
|
||||||
{challenge, response} = captcha
|
|
||||||
@count()
|
@count()
|
||||||
$.set 'captchas', @captchas
|
$.set 'captchas', @captchas
|
||||||
|
captcha.response
|
||||||
|
else if /\S/.test @nodes.input.value
|
||||||
|
(cb) =>
|
||||||
|
@submitCB = cb
|
||||||
|
@sendResponse()
|
||||||
else
|
else
|
||||||
challenge = @nodes.img.alt
|
null
|
||||||
if response = @nodes.input.value
|
|
||||||
if Conf['Auto-load captcha'] then @reload() else @destroy()
|
|
||||||
{challenge, response}
|
|
||||||
|
|
||||||
save: ->
|
sendResponse: ->
|
||||||
return unless /\S/.test(response = @nodes.input.value)
|
response = @nodes.input.value
|
||||||
|
if /\S/.test response
|
||||||
|
@conn.send {response}
|
||||||
|
@iframeUsed = true
|
||||||
|
|
||||||
|
save: (token) ->
|
||||||
@nodes.input.value = ''
|
@nodes.input.value = ''
|
||||||
@captchas.push
|
if @submitCB
|
||||||
challenge: @nodes.img.alt
|
@submitCB token
|
||||||
response: response
|
delete @submitCB
|
||||||
timeout: @timeout
|
if Conf['Auto-load captcha'] then @reload() else @destroy()
|
||||||
@count()
|
else
|
||||||
@reload()
|
$.forceSync 'captchas'
|
||||||
$.set 'captchas', @captchas
|
@captchas.push
|
||||||
|
response: token
|
||||||
|
timeout: @timeout
|
||||||
|
@count()
|
||||||
|
$.set 'captchas', @captchas
|
||||||
|
@reload()
|
||||||
|
|
||||||
|
error: (message) ->
|
||||||
|
@nodes.input.value = ''
|
||||||
|
QR.error "CAPTCHA Error: #{message}"
|
||||||
|
if @submitCB
|
||||||
|
@submitCB()
|
||||||
|
delete @submitCB
|
||||||
|
|
||||||
clear: ->
|
clear: ->
|
||||||
return unless @captchas.length
|
return unless @captchas.length
|
||||||
|
$.forceSync 'captchas'
|
||||||
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
|
||||||
@ -112,16 +150,17 @@ QR.captcha =
|
|||||||
@count()
|
@count()
|
||||||
$.set 'captchas', @captchas
|
$.set 'captchas', @captchas
|
||||||
|
|
||||||
load: ->
|
load: (src) ->
|
||||||
return unless @nodes.challenge.firstChild
|
{container, input, img} = @nodes
|
||||||
return unless challenge_image = $.id 'recaptcha_challenge_image'
|
@timeout = Date.now() + @lifetime
|
||||||
# -1 minute to give upload some time.
|
unless img
|
||||||
@timeout = Date.now() + @lifetime * $.SECOND - $.MINUTE
|
img = @nodes.img = new Image
|
||||||
challenge = @nodes.challenge.firstChild.value
|
$.one img, 'load', @afterSetup.bind @
|
||||||
@nodes.img.alt = challenge
|
$.add container, img
|
||||||
@nodes.img.src = challenge_image.src
|
img.src = src
|
||||||
@nodes.input.value = null
|
input.value = ''
|
||||||
@clear()
|
@clear()
|
||||||
|
setTimeout @reload.bind(@), @lifetime
|
||||||
|
|
||||||
count: ->
|
count: ->
|
||||||
count = if @captchas then @captchas.length else 0
|
count = if @captchas then @captchas.length else 0
|
||||||
@ -135,18 +174,20 @@ QR.captcha =
|
|||||||
" (#{count} cached captchas)"
|
" (#{count} cached captchas)"
|
||||||
@nodes.input.placeholder = placeholder
|
@nodes.input.placeholder = placeholder
|
||||||
@nodes.input.alt = count # For XTRM RICE.
|
@nodes.input.alt = count # For XTRM RICE.
|
||||||
|
clearTimeout @timeout
|
||||||
|
if @captchas.length
|
||||||
|
@timeout = setTimeout @clear.bind(@), @captchas[0].timeout - Date.now()
|
||||||
|
|
||||||
reload: (focus) ->
|
reload: (focus) ->
|
||||||
# Hack to prevent the input from being focused
|
@nodes.iframe.src = @iframeURL
|
||||||
$.globalEval 'Recaptcha.reload(); Recaptcha.should_focus = false;'
|
delete @iframeUsed
|
||||||
# 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()
|
||||||
else if e.keyCode is 13 and e.shiftKey
|
else if e.keyCode is 13 and e.shiftKey
|
||||||
@save()
|
@sendResponse()
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|||||||
@ -629,8 +629,8 @@ QR =
|
|||||||
err = 'Max limit of image replies has been reached.'
|
err = 'Max limit of image replies has been reached.'
|
||||||
|
|
||||||
if QR.captcha.isEnabled and !err
|
if QR.captcha.isEnabled and !err
|
||||||
{challenge, response} = QR.captcha.getOne()
|
captcha = QR.captcha.getOne()
|
||||||
err = 'No valid captcha.' unless response
|
err = 'No valid captcha.' unless captcha
|
||||||
|
|
||||||
QR.cleanNotifications()
|
QR.cleanNotifications()
|
||||||
if err
|
if err
|
||||||
@ -663,8 +663,6 @@ QR =
|
|||||||
textonly: textOnly
|
textonly: textOnly
|
||||||
mode: 'regist'
|
mode: 'regist'
|
||||||
pwd: QR.persona.pwd
|
pwd: QR.persona.pwd
|
||||||
recaptcha_challenge_field: challenge
|
|
||||||
recaptcha_response_field: response
|
|
||||||
|
|
||||||
options =
|
options =
|
||||||
responseType: 'document'
|
responseType: 'document'
|
||||||
@ -696,11 +694,29 @@ QR =
|
|||||||
QR.req.progress = "#{Math.round e.loaded / e.total * 100}%"
|
QR.req.progress = "#{Math.round e.loaded / e.total * 100}%"
|
||||||
QR.status()
|
QR.status()
|
||||||
|
|
||||||
QR.req = $.ajax "https://sys.4chan.org/#{g.BOARD}/post", options, extra
|
cb = (response) ->
|
||||||
|
extra.form.append 'g-recaptcha-response', response if response?
|
||||||
|
QR.req = $.ajax "https://sys.4chan.org/#{g.BOARD}/post", options, extra
|
||||||
|
QR.req.progress = '...'
|
||||||
|
|
||||||
|
if typeof captcha is 'function'
|
||||||
|
# Wait for captcha to be verified before submitting post.
|
||||||
|
QR.req =
|
||||||
|
progress: '...'
|
||||||
|
abort: -> cb = null
|
||||||
|
captcha (response) ->
|
||||||
|
if response
|
||||||
|
cb? response
|
||||||
|
else
|
||||||
|
delete QR.req
|
||||||
|
post.unlock()
|
||||||
|
QR.cooldown.auto = !!QR.captcha.captchas.length
|
||||||
|
QR.status()
|
||||||
|
else
|
||||||
|
cb captcha
|
||||||
|
|
||||||
# Starting to upload might take some time.
|
# Starting to upload might take some time.
|
||||||
# Provide some feedback that we're starting to submit.
|
# Provide some feedback that we're starting to submit.
|
||||||
QR.req.uploadStartTime = Date.now()
|
|
||||||
QR.req.progress = '...'
|
|
||||||
QR.status()
|
QR.status()
|
||||||
|
|
||||||
response: ->
|
response: ->
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user