Merge pull request #1 from ccd0/nobump

fix new captcha issue
This commit is contained in:
Bjorn Lim 2014-04-03 18:23:11 +08:00
commit 894682f067
12 changed files with 320 additions and 445 deletions

View File

@ -1,6 +1,16 @@
**MayhemYDG**:
- Fix captcha not refreshing.
- Fix captcha submission:<br>
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.
**duckness**:
- Merge changes from Mayhem fork
**ccd0**:
- Embedding for direct video links
- Merge changes from Mayhem fork
### v1.4.1
*2014-03-01*
**Spittie**
- Check image dimension before uploading

View File

@ -1,5 +1,5 @@
/*
* 4chan X - Version 1.4.1 - 2014-03-01
* 4chan X - Version 1.4.1 - 2014-04-02
*
* Licensed under the MIT license.
* https://github.com/Spittie/4chan-x/blob/master/LICENSE

View File

@ -2,9 +2,11 @@ Personal fork of Seaweed's 4chan X.
#### [Why 4chan X needs to access data on every site?](https://github.com/Spittie/4chan-x/wiki/Why-4chan-X-needs-to-access-data-from-every-website%3F)
##[Install](https://github.com/Spittie/4chan-x/raw/master/builds/4chan-X.user.js) (Firefox)
##[Install](https://github.com/Spittie/4chan-x/raw/master/builds/crx.crx) (Chrom*)
##[Install](../../raw/master/builds/4chan-X.user.js) (Firefox)
##[Install](../../raw/master/builds/crx.crx) (Chrom*)
<!---
##[Install](http://a.pomf.se/tkhuwm.xpi) (Firefox Mobile)
-->
## If you have any problems, try resetting your 4chan X settings

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1260,6 +1260,10 @@ div.boardTitle {
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/installgentoo.png", {encoding: "base64"}) %>') center left no-repeat!important;
padding-left: 18px;
}
.linkify.video {
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/video.png", {encoding: "base64"}) %>') center left no-repeat!important;
padding-left: 18px;
}
/* Gallery */
#a-gallery {

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

View File

@ -20,6 +20,9 @@ Linkify =
[-\w\d.@]+@[a-z\d.-]+\.[a-z\d]
)///i
@types = {}
@types[type.key] = type for type in @ordered_types
if Conf['Comment Expansion']
ExpandComment.callbacks.push @node
@ -146,9 +149,10 @@ Linkify =
services: (link) ->
href = link.href
for key, type of Linkify.types
for type in Linkify.ordered_types
continue unless match = type.regExp.exec href
return [key, match[1], match[2], link]
break if type.dummy
return [type.key, match[1], match[2], link]
return
@ -254,16 +258,16 @@ Linkify =
text = "[#{key}] #{@status}'d"
link.textContent = text if link
types:
audio:
ordered_types: [
key: 'audio'
regExp: /(.*\.(mp3|ogg|wav))$/
el: (a) ->
$.el 'audio',
controls: 'controls'
preload: 'auto'
src: a.dataset.uid
gist:
,
key: 'gist'
regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/
el: (a) ->
div = $.el 'iframe',
@ -273,27 +277,27 @@ Linkify =
api: (uid) -> "https://api.github.com/gists/#{uid}"
text: ({files}) ->
return file for file of files when files.hasOwnProperty file
image:
,
key: 'image'
regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/
style: 'border: 0; width: auto; height: auto;'
el: (a) ->
$.el 'div',
innerHTML: "<a target=_blank href='#{a.dataset.href}'><img src='#{a.dataset.href}'></a>"
InstallGentoo:
,
key: 'InstallGentoo'
regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/
el: (a) ->
$.el 'iframe',
src: "http://paste.installgentoo.com/view/embed/#{a.dataset.uid}"
Twitter:
,
key: 'Twitter'
regExp: /.*twitter.com\/(.+\/status\/\d+)/
el: (a) ->
$.el 'iframe',
src: "https://twitframe.com/show?url=https://twitter.com/#{a.dataset.uid}"
LiveLeak:
,
key: 'LiveLeak'
regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/
el: (a) ->
el = $.el 'iframe',
@ -303,8 +307,8 @@ Linkify =
frameborder: "0"
el.setAttribute "allowfullscreen", "true"
el
MediaCrush:
,
key: 'MediaCrush'
regExp: /.*(?:mediacru.sh\/)([0-9a-z_]+)/i
style: 'border: 0;'
el: (a) ->
@ -335,20 +339,20 @@ Linkify =
else
"ERROR: No valid filetype."
el
pastebin:
,
key: 'pastebin'
regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/
el: (a) ->
div = $.el 'iframe',
src: "http://pastebin.com/embed_iframe.php?i=#{a.dataset.uid}"
gfycat:
,
key: 'gfycat'
regExp: /.*gfycat.com\/(?:iframe\/)?(\S*)/
el: (a) ->
div = $.el 'iframe',
src: "http://gfycat.com/iframe/#{a.dataset.uid}"
SoundCloud:
,
key: 'SoundCloud'
regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/
style: 'height: auto; width: 500px; display: inline-block;'
el: (a) ->
@ -364,15 +368,15 @@ Linkify =
title:
api: (uid) -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{uid}"
text: (_) -> _.title
StrawPoll:
,
key: 'StrawPoll'
regExp: /strawpoll\.me\/(?:embed_\d+\/)?(\d+)/
style: 'border: 0; width: 600px; height: 406px;'
el: (a) ->
$.el 'iframe',
src: "http://strawpoll.me/embed_1/#{a.dataset.uid}"
TwitchTV:
,
key: 'TwitchTV'
regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/
style: "border: none; width: 640px; height: 360px;"
el: (a) ->
@ -396,15 +400,15 @@ Linkify =
<param name="movie" value="http://www.twitch.tv/widgets/live_embed_player.swf" />
<param name="flashvars" value="hostname=www.twitch.tv&channel=#{channel}&auto_play=true&start_volume=25" />
"""
Vocaroo:
,
key: 'Vocaroo'
regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/
style: 'border: 0; width: 150px; height: 45px;'
el: (a) ->
$.el 'object',
innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=#{a.dataset.uid.replace /^i\//, ''}&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
Vimeo:
,
key: 'Vimeo'
regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/
el: (a) ->
$.el 'iframe',
@ -412,15 +416,15 @@ Linkify =
title:
api: (uid) -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{uid}"
text: (_) -> _.title
Vine:
,
key: 'Vine'
regExp: /.*(?:vine.co\/)([^#\&\?]*).*/
style: 'border: none; width: 500px; height: 500px;'
el: (a) ->
$.el 'iframe',
src: "https://vine.co/#{a.dataset.uid}/card"
YouTube:
,
key: 'YouTube'
regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/
el: (a) ->
el = $.el 'iframe',
@ -430,4 +434,22 @@ Linkify =
title:
api: (uid) -> "https://gdata.youtube.com/feeds/api/videos/#{uid}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"
text: (data) -> data.entry.title.$t
,
# dummy entries: not implemented yet but included to prevent them being wrongly embedded as a subsequent type
key: 'Loopvid'
regExp: /.*loopvid.appspot.com\/.*/
dummy: true
,
key: 'MediaFire'
regExp: /.*mediafire.com\/.*/
dummy: true
,
key: 'video'
regExp: /(.*\.(ogv|webm|mp4))$/
el: (a) ->
$.el 'video',
controls: 'controls'
preload: 'auto'
src: a.dataset.uid
]

View File

@ -1,8 +1,7 @@
QR.captcha =
init: ->
return if d.cookie.indexOf('pass_enabled=1') >= 0
container = $.id 'captchaContainer'
return unless @isEnabled = !!container
return unless @isEnabled = !!$.id 'captchaContainer'
$.globalEval 'loadRecaptcha()' if Conf['Auto-load captcha']
@ -10,11 +9,9 @@ QR.captcha =
className: 'captcha-img'
title: 'Reload reCAPTCHA'
innerHTML: '<img>'
hidden: true
input = $.el 'input',
className: 'captcha-input field'
title: 'Verification'
placeholder: 'Focus to load reCAPTCHA'
autocomplete: 'off'
spellcheck: false
tabIndex: 45
@ -22,17 +19,22 @@ QR.captcha =
img: imgContainer.firstChild
input: input
$.on input, 'focus', @setup
$.on input, 'blur', QR.focusout
$.on input, 'focus', QR.focusin
$.addClass QR.nodes.el, 'has-captcha'
$.after QR.nodes.com.parentNode, [imgContainer, input]
@setupObserver = new MutationObserver @afterSetup
@setupObserver.observe container, childList: true
@beforeSetup()
@afterSetup() # reCAPTCHA might have loaded before the QR.
beforeSetup: ->
{img, input} = @nodes
img.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: ->
$.globalEval 'loadRecaptcha()'
afterSetup: ->
@ -40,89 +42,37 @@ QR.captcha =
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
input.placeholder = 'Verification'
$.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
subtree: true
attributes: true
QR.captcha.load()
sync: (captchas) ->
QR.captcha.captchas = captchas
QR.captcha.count()
destroy: ->
$.globalEval 'Recaptcha.destroy()'
@beforeSetup()
getOne: ->
@clear()
if captcha = @captchas.shift()
{challenge, response} = captcha
@count()
$.set 'captchas', @captchas
else
challenge = @nodes.img.alt
if response = @nodes.input.value then @reload()
if response
response = response.trim()
challenge = @nodes.img.alt
response = @nodes.input.value.trim()
if response and !/\s/.test response
# one-word-captcha:
# If there's only one word, duplicate it.
response = "#{response} #{response}" unless /\s/.test response
response = "#{response} #{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.length
now = Date.now()
for captcha, i in @captchas
break if captcha.timeout > now
return unless i
@captchas = @captchas[i..]
@count()
$.set 'captchas', @captchas
load: ->
return unless @nodes.challenge.firstChild
# -1 minute to give upload some time.
@timeout = Date.now() + @lifetime * $.SECOND - $.MINUTE
challenge = @nodes.challenge.firstChild.value
@nodes.img.alt = challenge
@nodes.img.src = "//www.google.com/recaptcha/api/image?c=#{challenge}"
@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 # For XTRM RICE.
reload: (focus) ->
# the 't' argument prevents the input from being focused
$.globalEval 'Recaptcha.reload("t")'
@ -132,8 +82,6 @@ QR.captcha =
keydown: (e) ->
if e.keyCode is 8 and not @nodes.input.value
@reload()
else if e.keyCode is 13 and e.shiftKey
@save()
else
return
e.preventDefault()

View File

@ -663,6 +663,9 @@ QR =
onerror: (err, url, line) ->
# Connection error, or www.4chan.org/banned
delete QR.req
if QR.captcha.isEnabled
QR.captcha.destroy()
QR.captcha.setup()
post.unlock()
QR.cooldown.auto = false
QR.status()
@ -699,6 +702,7 @@ QR =
{req} = QR
delete QR.req
QR.captcha.destroy() if QR.captcha.isEnabled
post = QR.posts[0]
post.unlock()
@ -730,23 +734,12 @@ QR =
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.'
# 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
QR.cooldown.auto = false
# Too many frequent mistyped captchas will auto-ban you!
# On connection error, the post most likely didn't go through.
QR.cooldown.set delay: 2
else if err.textContent and m = err.textContent.match /wait\s+(\d+)\s+second/i
QR.cooldown.auto = if QR.captcha.isEnabled
!!QR.captcha.captchas.length
else
true
QR.cooldown.auto = !QR.captcha.isEnabled
QR.cooldown.set delay: m[1]
else # stop auto-posting
QR.cooldown.auto = false
@ -786,18 +779,6 @@ QR =
# Enable auto-posting if we have stuff left to post, disable it otherwise.
postsCount = QR.posts.length - 1
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
QR.close()

View File

@ -91,6 +91,8 @@ QR.post = class
return unless @ is QR.selected
for name in ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag'] when node = QR.nodes[name]
node.disabled = lock
if QR.captcha.isEnabled
QR.captcha.nodes.input.disabled = lock
@nodes.rm.style.visibility = if lock then 'hidden' else ''
(if lock then $.off else $.on) QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput
@nodes.spoiler.disabled = lock