Merge branch 'v3'

Conflicts:
	CHANGELOG.md
	LICENSE
	builds/appchan-x.user.js
	builds/crx/manifest.json
	builds/crx/script.js
	package.json
	src/General/Navigate.coffee
	src/Miscellaneous/CatalogLinks.coffee
	src/Posting/QR.coffee
This commit is contained in:
Zixaphir 2014-05-27 12:39:01 -07:00
commit 2efa358131
21 changed files with 622 additions and 566 deletions

View File

@ -1,5 +1,72 @@
<<<<<<< HEAD
### v2.9.26 ### v2.9.26
*2014-05-07* *2014-05-07*
=======
**MayhemYDG**:
- [Security fix](https://github.com/MayhemYDG/4chan-x/issues/1634).
### v1.7.33
*2014-05-10*
**DamonGant**
- Add Innovandalism Archive.
**ccd0**
- Update archive list.
- Add "disabled" option when Foolz Beta is the only choice.
### v1.7.32
*2014-05-10*
**Zixaphir, ccd0**
- Bug fixes in linkification/embedding.
**ccd0**
- Begin refactoring code to reduce potential for introducing Javascript injection bugs.
### v1.7.31
*2014-05-08*
**Zixaphir**
- Refactoring, bug fixes.
**ccd0**
- Fix some potential Javascript injection issues.
- Bug fixes.
### v1.7.30
*2014-05-05*
**thebladeee**
- Update archives.
### v1.7.29
*2014-05-03*
**ccd0**:
- If the original post form not hidden, it is expanded (except on the catalog page).
- 4chan's horizontal rules are no longer hidden. If you want to hide them as before, add the old code to your custom CSS:
```
body > hr,
#blotter hr,
.desktop > hr,
#delform > hr,
#content > hr {
display: none;
}
:root.index .board > hr:last-of-type,
:root.thread .board > hr {
border: 0px;
margin: 0px;
}
```
### v1.7.28
*2014-05-03*
**ccd0**:
- Copy Mayhem's fix for 4chan post form changes.
>>>>>>> v3
**zixaphir** **zixaphir**
- Fixed (maybe?) a vulnerability in the post parser that allowed playful users to inject scripts or elements into posts, which could be used maliciously. - Fixed (maybe?) a vulnerability in the post parser that allowed playful users to inject scripts or elements into posts, which could be used maliciously.

View File

@ -1,5 +1,5 @@
/* /*
* appchan x - Version 2.9.26 - 2014-05-11 * appchan x - Version 2.9.26 - 2014-05-27
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE * https://github.com/zixaphir/appchan-x/blob/master/LICENSE

View File

@ -1,6 +1,6 @@
// ==UserScript== // ==UserScript==
// @name 4chan X // @name 4chan X
// @version 1.7.27 // @version 1.7.33
// @minGMVer 1.14 // @minGMVer 1.14
// @minFFVer 26 // @minFFVer 26
// @namespace 4chan-X // @namespace 4chan-X

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'> <gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
<app appid='lacclbnghgdicfifcamcmcnilckjamag'> <app appid='lacclbnghgdicfifcamcmcnilckjamag'>
<updatecheck codebase='https://ccd0.github.io/4chan-x/builds/crx.crx' version='1.7.27' /> <updatecheck codebase='https://ccd0.github.io/4chan-x/builds/crx.crx' version='1.7.33' />
</app> </app>
</gupdate> </gupdate>

View File

@ -18,9 +18,9 @@
outline: none; outline: none;
transition: color .25s, border-color .25s, flex .25s; transition: color .25s, border-color .25s, flex .25s;
} }
.field::-moz-placeholder, .field::-moz-placeholder {
.field:hover::-moz-placeholder { color: #AAA;
color: #AAA !important; opacity: 1;
} }
.field:hover { .field:hover {
border-color: #999; border-color: #999;

View File

@ -7,9 +7,9 @@ Redirect =
archives = {} archives = {}
for data in Redirect.archives for data in Redirect.archives
{name, boards, files, software} = data {name, boards, files, software, withCredentials} = data
archives[name] = data archives[name] = data
for boardID in boards for boardID in boards when !withCredentials
o.thread[boardID] = data unless boardID of o.thread o.thread[boardID] = data unless boardID of o.thread
o.post[boardID] = data unless boardID of o.post or software isnt 'foolfuuka' o.post[boardID] = data unless boardID of o.post or software isnt 'foolfuuka'
o.file[boardID] = data unless boardID of o.file or boardID not in files o.file[boardID] = data unless boardID of o.file or boardID not in files

View File

@ -6,7 +6,7 @@
"https": true, "https": true,
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["a", "biz", "co", "diy", "gd", "jp", "m", "sci", "sp", "tg", "tv", "vg", "vp", "vr", "wsg"], "boards": ["a", "biz", "co", "diy", "gd", "jp", "m", "sci", "sp", "tg", "tv", "vg", "vp", "vr", "wsg"],
"files": ["a", "biz", "gd", "diy", "jp", "m", "sci", "tg", "vg", "vp", "vr", "wsg"] "files": ["a", "biz", "diy", "gd", "jp", "m", "sci", "tg", "vg", "vp", "vr", "wsg"]
}, { }, {
"uid": 1, "uid": 1,
"name": "NSFW Foolz", "name": "NSFW Foolz",
@ -65,7 +65,7 @@
"uid": 8, "uid": 8,
"name": "Rebecca Black Tech", "name": "Rebecca Black Tech",
"domain": "rbt.asia", "domain": "rbt.asia",
"http": true, "http": false,
"https": true, "https": true,
"software": "fuuka", "software": "fuuka",
"boards": ["cgl", "g", "mu", "w"], "boards": ["cgl", "g", "mu", "w"],
@ -78,7 +78,7 @@
"https": false, "https": false,
"software": "fuuka", "software": "fuuka",
"boards": ["an", "fit", "k", "mlp", "r9k", "toy"], "boards": ["an", "fit", "k", "mlp", "r9k", "toy"],
"files": ["an", "fit", "k", "r9k", "toy"] "files": ["an", "fit", "k", "mlp", "r9k", "toy"]
}, { }, {
"uid": 10, "uid": 10,
"name": "warosu", "name": "warosu",
@ -115,6 +115,15 @@
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["g", "t"], "boards": ["g", "t"],
"files": ["g", "t"] "files": ["g", "t"]
}, {
"uid": 19,
"name": "Innovandalism Archive",
"domain": "boards.innovandalism.eu",
"http": true,
"https": false,
"software": "foolfuuka",
"boards": ["v"],
"files": []
}, { }, {
"uid": 13, "uid": 13,
"name": "Foolz Beta", "name": "Foolz Beta",
@ -125,4 +134,13 @@
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["a", "biz", "co", "d", "diy", "gd", "jp", "m", "s4s", "sci", "sp", "tg", "tv", "u", "vg", "vp", "vr", "wsg"], "boards": ["a", "biz", "co", "d", "diy", "gd", "jp", "m", "s4s", "sci", "sp", "tg", "tv", "u", "vg", "vp", "vr", "wsg"],
"files": ["a", "biz", "d", "diy", "gd", "jp", "m", "s4s", "sci", "tg", "u", "vg", "vp", "vr", "wsg"] "files": ["a", "biz", "d", "diy", "gd", "jp", "m", "s4s", "sci", "tg", "u", "vg", "vp", "vr", "wsg"]
}, {
"uid": 19,
"name": "Innovandalism Archive",
"domain": "boards.innovandalism.eu",
"http": true,
"https": false,
"software": "foolfuuka",
"boards": ["v"],
"files": []
}] }]

View File

@ -47,7 +47,7 @@ Build =
name: data.filename + data.ext name: data.filename + data.ext
timestamp: "#{data.tim}#{data.ext}" timestamp: "#{data.tim}#{data.ext}"
url: if boardID is 'f' url: if boardID is 'f'
"//i.4cdn.org/#{boardID}/#{data.filename}#{data.ext}" "//i.4cdn.org/#{boardID}/#{encodeURIComponent data.filename}#{data.ext}"
else else
"//i.4cdn.org/#{boardID}/#{data.tim}#{data.ext}" "//i.4cdn.org/#{boardID}/#{data.tim}#{data.ext}"
height: data.h height: data.h
@ -150,7 +150,7 @@ Build =
imgSrc = if boardID is 'f' imgSrc = if boardID is 'f'
'' ''
else else
"<a class='fileThumb#{if file.isSpoiler then ' imgspoiler' else ''}' href='#{file.url}' target=_blank>" + "<a class='fileThumb#{if file.isSpoiler then ' imgspoiler' else ''}' href=\"#{file.url}\" target=_blank>" +
"<img src='#{fileThumb}' alt='#{fileSize}' data-md5=#{file.MD5} style='height: #{file.theight}px; width: #{file.twidth}px;'>" + "<img src='#{fileThumb}' alt='#{fileSize}' data-md5=#{file.MD5} style='height: #{file.theight}px; width: #{file.twidth}px;'>" +
"</a>" "</a>"
@ -166,7 +166,7 @@ Build =
fileDims = if file.name[-3..] is 'pdf' then 'PDF' else "#{file.width}x#{file.height}" fileDims = if file.name[-3..] is 'pdf' then 'PDF' else "#{file.width}x#{file.height}"
fileInfo = "<div class=fileText #{if file.isSpoiler then "title='#{filename}'" else ''}>File: " + fileInfo = "<div class=fileText #{if file.isSpoiler then "title='#{filename}'" else ''}>File: " +
"<a href='#{file.url}' #{if filename isnt shortFilename and !file.isSpoiler then " title='#{filename}'" else ''} target=_blank>#{if file.isSpoiler then 'Spoiler Image' else shortFilename}</a>" + "<a href=\"#{file.url}\" #{if filename isnt shortFilename and !file.isSpoiler then " title='#{filename}'" else ''} target=_blank>#{if file.isSpoiler then 'Spoiler Image' else shortFilename}</a>" +
" (#{fileSize}, #{fileDims})</div>" " (#{fileSize}, #{fileDims})</div>"
fileHTML = "<div class=file>#{fileInfo}#{imgSrc}</div>" fileHTML = "<div class=file>#{fileInfo}#{imgSrc}</div>"
@ -284,7 +284,7 @@ Build =
pageCount = Index.liveThreadData.keys.indexOf("#{thread.ID}") // Index.threadsNumPerPage + 1 pageCount = Index.liveThreadData.keys.indexOf("#{thread.ID}") // Index.threadsNumPerPage + 1
subject = if thread.OP.info.subject subject = if thread.OP.info.subject
"<div class='subject'>#{thread.OP.info.subject}</div>" "<div class='subject'>#{thread.OP.nodes.subject.innerHTML}</div>"
else else
'' ''
comment = thread.OP.nodes.comment.innerHTML.replace /(<br>\s*){2,}/g, '<br>' comment = thread.OP.nodes.comment.innerHTML.replace /(<br>\s*){2,}/g, '<br>'

View File

@ -91,6 +91,7 @@ Index =
@navLinks = $.el 'div', @navLinks = $.el 'div',
className: 'navLinks' className: 'navLinks'
innerHTML: <%= importHTML('Features/Index-navlinks') %> innerHTML: <%= importHTML('Features/Index-navlinks') %>
@timeEl = $ 'time#index-last-refresh', @navLinks
@searchInput = $ '#index-search', @navLinks @searchInput = $ '#index-search', @navLinks
@ -114,7 +115,8 @@ Index =
@currentPage = @getCurrentPage() @currentPage = @getCurrentPage()
$.on d, 'scroll', Index.scroll $.on d, 'scroll', @scroll
$.on window, 'focus', @updateIfNeeded
$.on @pagelist, 'click', @cb.pageNav $.on @pagelist, 'click', @cb.pageNav
returnLink = $.el 'a', returnLink = $.el 'a',
@ -221,11 +223,12 @@ Index =
thread = g.threads[@parentNode.dataset.fullID] thread = g.threads[@parentNode.dataset.fullID]
if e.shiftKey if e.shiftKey
PostHiding.toggle thread.OP PostHiding.toggle thread.OP
e.preventDefault()
else if e.altKey else if e.altKey
Index.togglePin thread Index.togglePin thread
e.preventDefault()
else else
Navigate.navigate.call @ Navigate.navigate.call @, e
e.preventDefault()
onOver: (e) -> onOver: (e) ->
# 4chan's less than stellar CSS forces us to include a .post and .postInfo # 4chan's less than stellar CSS forces us to include a .post and .postInfo
@ -297,7 +300,7 @@ Index =
setupNavLinks: -> setupNavLinks: ->
for el in $$ '.navLinks.desktop > a' for el in $$ '.navLinks.desktop > a'
if el.getAttribute('href') is '.././catalog' if /\/catalog$/.test el.pathname
el.href = '.././' el.href = '.././'
$.on el, 'click', -> $.on el, 'click', ->
switch @textContent switch @textContent
@ -508,6 +511,18 @@ Index =
else else
"#{hiddenCount} hidden threads" "#{hiddenCount} hidden threads"
updateIfNeeded: ->
{timeEl} = Index
needed =
# we're on the index,
g.VIEW is 'index' and
# not currently refreshing
!Index.req and
timeEl.dataset.utc and
# more than 10 minutes have elapsed since the last refresh.
timeEl.dataset.utc < Date.now() - (10 * $.MINUTE)
Index.update() if needed
update: (pageNum) -> update: (pageNum) ->
return unless navigator.onLine return unless navigator.onLine
if g.VIEW is 'thread' if g.VIEW is 'thread'
@ -585,7 +600,7 @@ Index =
new Notice 'error', 'Index refresh failed.', 1 new Notice 'error', 'Index refresh failed.', 1
return return
timeEl = $ 'time#index-last-refresh', Index.navLinks {timeEl} = Index
timeEl.dataset.utc = Date.parse req.getResponseHeader 'Last-Modified' timeEl.dataset.utc = Date.parse req.getResponseHeader 'Last-Modified'
RelativeDates.update timeEl RelativeDates.update timeEl
Index.scrollToIndex() Index.scrollToIndex()

View File

@ -87,11 +87,6 @@ Main =
Report.init() Report.init()
return return
when 'i.4cdn.org' when 'i.4cdn.org'
if Conf['Loop in New Tab'] and video = $ 'video'
Video.configure video
$.on video, 'click', ->
if !video.controls
if video.paused then video.play() else video.pause()
$.ready -> $.ready ->
if Conf['404 Redirect'] and d.title in ['4chan - Temporarily Offline', '4chan - 404 Not Found'] if Conf['404 Redirect'] and d.title in ['4chan - Temporarily Offline', '4chan - 404 Not Found']
Redirect.init() Redirect.init()
@ -100,6 +95,11 @@ Main =
boardID: g.BOARD.ID boardID: g.BOARD.ID
filename: pathname[pathname.length - 1] filename: pathname[pathname.length - 1]
location.replace URL if URL location.replace URL if URL
else if Conf['Loop in New Tab'] and video = $ 'video'
Video.configure video
if !video.controls
$.on video, 'click', ->
if video.paused then video.play() else video.pause()
return return
# c.time 'All initializations' # c.time 'All initializations'

View File

@ -196,7 +196,7 @@ Navigate =
return if @hostname isnt 'boards.4chan.org' or window.location.hostname is 'rs.4chan.org' return if @hostname isnt 'boards.4chan.org' or window.location.hostname is 'rs.4chan.org'
if e if e
if e.shiftKey or e.ctrlKey or (e.type is 'click' and e.button isnt 0) # Not simply a left click if e.shiftKey or e.ctrlKey or (e.type is 'click' and e.button isnt 0) # Not simply a left click
Navigate.setMode @ unless e?.button is 2 # Right Click Navigate.setMode @ unless e.button is 2 # Right Click
return return
if @pathname is Navigate.path if @pathname is Navigate.path

View File

@ -266,15 +266,21 @@ Settings =
$.on $.id('apply-css'), 'click', Settings.usercss $.on $.id('apply-css'), 'click', Settings.usercss
archBoards = {} archBoards = {}
for {name, boards, files, software} in Redirect.archives for {name, boards, files, software, withCredentials} in Redirect.archives
for boardID in boards for boardID in boards
o = archBoards[boardID] or= o = archBoards[boardID] or=
thread: [] thread: [[], []]
post: [] post: [[], []]
file: [] file: [[], []]
o.thread.push name i = +!!withCredentials
o.post.push name if software is 'foolfuuka' o.thread[i].push name
o.file.push name if boardID in files o.post[i].push name if software is 'foolfuuka'
o.file[i].push name if boardID in files
for boardID, o of archBoards
for item in ['thread', 'post', 'file']
if o[item][0].length is 0 and o[item][1].length isnt 0
o[item][0].push 'disabled'
o[item] = o[item][0].concat(o[item][1])
rows = [] rows = []
boardOptions = [] boardOptions = []

View File

@ -69,24 +69,9 @@ a {
border-radius: 3px; border-radius: 3px;
padding: 0px 2px; padding: 0px 2px;
} }
body > hr,
#blotter hr,
.desktop > hr,
#delform > hr,
#content > hr {
display: none;
}
:root.index .board > hr:last-of-type,
:root.thread .board > hr {
border: 0px;
margin: 0px;
}
.ad-plea { .ad-plea {
display: none; display: none;
} }
.ad-cnt {
margin: 10px !important;
}
/* 4chan style fixes */ /* 4chan style fixes */
.opContainer, .op { .opContainer, .op {
@ -904,13 +889,16 @@ span.hide-announcement {
/* QR */ /* QR */
:root.hide-original-post-form #postForm, :root.hide-original-post-form #postForm,
:root.hide-original-post-form .postingMode, :root.hide-original-post-form #togglePostFormLink,
:root.hide-original-post-form #togglePostForm, :root:not(.catalog) #togglePostFormLink,
#qr.autohide:not(.focus):not(:hover):not(:active) > form, #qr.autohide:not(.focus):not(:hover):not(:active) > form,
.thread #qr select[data-name=thread], .thread #qr select[data-name=thread],
#file-n-submit:not(.has-file) #qr-filerm { #file-n-submit:not(.has-file) #qr-filerm {
display: none; display: none;
} }
:root:not(.hide-original-post-form):not(.catalog) #postForm {
display: table;
}
#qr select, #qr select,
#dump-button, #dump-button,
#url-button, #url-button,

View File

@ -24,7 +24,7 @@
"javascript:quote(#{postID})" "javascript:quote(#{postID})"
else else
"/#{boardID}/thread/#{threadID}#q#{postID}" "/#{boardID}/thread/#{threadID}#q#{postID}"
}' title='Quote this post'>#{postID}</a> }' title='Reply to this post'>#{postID}</a>
#{pageIcon + sticky + closed + replyLink} #{pageIcon + sticky + closed + replyLink}
</span> </span>
</div> </div>

View File

@ -10,14 +10,13 @@ Linkify =
if Conf['Embedding'] or Conf['Link Title'] if Conf['Embedding'] or Conf['Link Title']
@embedProcess = Function 'link', @embedProcess = Function 'link',
"var data = this.services(link); """
if (data) { var data = this.services(link);
#{ if (data) {#{
(if Conf['Embedding'] then 'this.embed(data);\n' else '') + (if Conf['Embedding'] then 'this.embed(data); ' else '') +
if Conf['Link Title'] then 'this.title(data);' else '' if Conf['Link Title'] then 'data.push(post); this.title(data);' else ''
} }}
} """
"
Post.callbacks.push Post.callbacks.push
name: 'Linkify' name: 'Linkify'
@ -156,7 +155,7 @@ Linkify =
return return
embed: (data) -> embed: (data) ->
[key, uid, options, link] = data [key, uid, options, link, post] = data
href = link.href href = link.href
embed = $.el 'a', embed = $.el 'a',
className: 'embedder' className: 'embedder'
@ -164,7 +163,6 @@ Linkify =
textContent: '(embed)' textContent: '(embed)'
embed.dataset[name] = value for name, value of {key, href, uid, options} embed.dataset[name] = value for name, value of {key, href, uid, options}
embed.dataset.nodedata = link.innerHTML
$.addClass link, "#{embed.dataset.key}" $.addClass link, "#{embed.dataset.key}"
@ -173,33 +171,30 @@ Linkify =
Linkify.cb.toggle.call embed if Conf['Auto-embed'] Linkify.cb.toggle.call embed if Conf['Auto-embed']
data.push embed
title: (data) -> title: (data) ->
[key, uid, options, link, embed] = data [key, uid, options, link, post] = data
return unless service = Linkify.types[key].title return unless service = Linkify.types[key].title
titles = Conf['CachedTitles'] titles = Conf['CachedTitles']
if title = titles[uid] if title = titles[uid]
# Auto-embed may destroy our links. link.textContent = title[0]
if link
link.textContent = title[0]
if Conf['Embedding']
embed.dataset.title = title[0]
else else
try try
$.cache service.api(uid), (-> Linkify.cb.title @, data), responseType: 'json' $.cache service.api(uid), (-> Linkify.cb.title @, data), responseType: 'json'
catch err catch err
if link link.innerHTML = '<span class="warning">Title Link Blocked</span> (are you using NoScript?)</a>'
link.innerHTML = "[#{key}] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>" $.prepend link, $.tn "[#{key}] "
return return
cb: cb:
toggle: -> toggle: ->
[string, @textContent] = if $.hasClass @, "embedded" if $.hasClass @, "embedded"
['unembed', '(embed)'] $.rm @previousElementSibling
@previousElementSibling.hidden = false
@textContent = '(embed)'
else else
['embed', '(unembed)'] @previousElementSibling.hidden = true
$.replace @previousElementSibling, Linkify.cb[string] @ $.before @, Linkify.cb.embed @
@textContent = '(unembed)'
$.toggleClass @, 'embedded' $.toggleClass @, 'embedded'
embed: (a) -> embed: (a) ->
@ -214,21 +209,8 @@ Linkify =
return el return el
unembed: (a) ->
# Recreate the original link.
el = $.el 'a',
rel: 'nofollow noreferrer'
target: 'blank'
className: 'linkify'
href: a.dataset.href
innerHTML: a.dataset.title or a.dataset.nodedata
$.addClass el, a.dataset.key
return el
title: (req, data) -> title: (req, data) ->
[key, uid, options, link, embed] = data [key, uid, options, link, post] = data
{status} = req {status} = req
service = Linkify.types[key].title service = Linkify.types[key].title
@ -243,8 +225,11 @@ Linkify =
"#{status}'d" "#{status}'d"
}" }"
embed.dataset.title = text if Conf['Embedding'] and status in [200, 304] link.textContent = text
link.textContent = text if link for post2 in post.clones
for link2 in $$ 'a', post2.nodes.comment when link2.href is link.href
link2.textContent = text
return
ordered_types: [ ordered_types: [
key: 'audio' key: 'audio'
@ -271,8 +256,10 @@ Linkify =
regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/ regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/
style: 'border: 0; width: auto; height: auto;' style: 'border: 0; width: auto; height: auto;'
el: (a) -> el: (a) ->
$.el 'div', el = $.el 'div'
innerHTML: "<a target=_blank href='#{a.dataset.href}'><img src='#{a.dataset.href}'></a>" el.innerHTML = '<a target="_blank"><img></a>'
el.firstChild.href = el.firstChild.firstChild.src = a.dataset.href
el
, ,
key: 'InstallGentoo' key: 'InstallGentoo'
regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/ regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/
@ -298,35 +285,36 @@ Linkify =
el el
, ,
key: 'MediaCrush' key: 'MediaCrush'
regExp: /.*(?:mediacru.sh\/)([0-9a-z_]+)/i regExp: /.*(?:mediacru.sh\/)([0-9a-z_-]+)/i
style: 'border: 0;' style: 'border: 0;'
el: (a) -> el: (a) ->
el = $.el 'div' el = $.el 'div'
$.cache "https://mediacru.sh/#{a.dataset.uid}.json", -> $.cache "https://mediacru.sh/#{a.dataset.uid}.json", ->
{status} = @ {status} = @
return div.innerHTML = "ERROR #{status}" unless status in [200, 304] return el.textContent = "ERROR #{status}" unless status in [200, 304]
{files} = @response {files} = @response
for type in ['video/mp4', 'video/ogv', 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg', 'image/svg', 'audio/mpeg'] for type in ['video/mp4', 'video/webm', 'video/ogv', 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg', 'audio/mpeg', 'audio/ogg']
for file in files for file in files
if file.type is type if file.type is type
embed = file embed = file
break break
break if embed break if embed
return div.innerHTML = "ERROR: Not a valid filetype" unless embed return div.textContent = "ERROR: Not a valid filetype" unless embed
el.innerHTML = switch embed.type switch embed.type
when 'video/mp4', 'video/ogv' then """ when 'video/mp4', 'video/webm', 'video/ogv'
<video autoplay loop> el.innerHTML = '<video autoplay loop><source type="video/mp4"><source type="video/webm"><source type="video/ogg"></video>'
<source src="https://mediacru.sh/#{a.dataset.uid}.mp4" type="video/mp4;"> for ext, i in ['mp4', 'webm', 'ogv']
<source src="https://mediacru.sh/#{a.dataset.uid}.ogv" type="video/ogg; codecs='theora, vorbis'"> el.firstChild.children[i].src = "https://mediacru.sh/#{a.dataset.uid}.#{ext}"
</video>""" when 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg'
when 'image/png', 'image/gif', 'image/jpeg' el.innerHTML = '<a target="_blank"><img></a>'
"<a target=_blank href='#{a.dataset.href}'><img src='https://mediacru.sh/#{file.file}'></a>" el.firstChild.href = a.dataset.href
when 'image/svg', 'image/svg+xml' el.firstChild.firstChild.src = "https://mediacru.sh/#{file.file}"
"<embed src='https://mediacru.sh/#{file.file}' type='image/svg+xml' />" when 'audio/mpeg', 'audio/ogg'
when 'audio/mpeg' el.innerHTML = '<audio controls><source type="audio/ogg"><source type="audio/mpeg"></audio>'
"<audio controls><source src='https://mediacru.sh/#{file.file}'></audio>" for ext, i in ['ogg', 'mp3']
el.firstChild.children[i].src = "https://mediacru.sh/#{a.dataset.uid}.#{ext}"
else else
"ERROR: No valid filetype." el.textContent = "ERROR: No valid filetype."
el el
, ,
key: 'pastebin' key: 'pastebin'
@ -343,19 +331,12 @@ Linkify =
, ,
key: 'SoundCloud' key: 'SoundCloud'
regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/ regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/
style: 'height: auto; width: 500px; display: inline-block;' style: 'border: 0; width: 500px; height: 400px;'
el: (a) -> el: (a) ->
div = $.el 'div', $.el 'iframe',
className: "soundcloud" src: "//w.soundcloud.com/player/?visual=true&show_comments=false&url=https%3A%2F%2Fsoundcloud.com%2F#{encodeURIComponent a.dataset.uid}"
name: "soundcloud"
$.ajax(
"//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{a.dataset.uid}"
onloadend: ->
div.innerHTML = JSON.parse(@responseText).html
false)
div
title: title:
api: (uid) -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{uid}" api: (uid) -> "//soundcloud.com/oembed?format=json&url=https%3A%2F%2Fsoundcloud.com%2F#{encodeURIComponent uid}"
text: (_) -> _.title text: (_) -> _.title
, ,
key: 'StrawPoll' key: 'StrawPoll'
@ -369,26 +350,21 @@ Linkify =
regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/ regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/
style: "border: none; width: 640px; height: 360px;" style: "border: none; width: 640px; height: 360px;"
el: (a) -> el: (a) ->
if result = /(\w+)\/(?:[a-z]\/)?(\d+)/i.exec a.dataset.uid if result = /(\w+)\/([bc])\/(\d+)/i.exec a.dataset.uid
[_, channel, chapter] = result [_, channel, type, id] = result
idparam = {'b': 'archive_id', 'c': 'chapter_id'}
$.el 'object', obj = $.el 'object',
data: 'http://www.twitch.tv/widgets/archive_embed_player.swf' data: 'http://www.twitch.tv/widgets/archive_embed_player.swf'
innerHTML: """ obj.innerHTML = '<param name="allowFullScreen" value="true"><param name="flashvars">'
<param name='allowFullScreen' value='true' /> obj.children[1].value = "channel=#{channel}&start_volume=25&auto_play=false&#{idparam[type]}=#{id}"
<param name='flashvars' value='channel=#{channel}&start_volume=25&auto_play=false#{if chapter then "&chapter_id=" + chapter else ""}' /> obj
"""
else else
channel = (/(\w+)/.exec a.dataset.uid)[0] channel = (/(\w+)/.exec a.dataset.uid)[0]
obj = $.el 'object',
$.el 'object',
data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=#{channel}" data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=#{channel}"
innerHTML: """ obj.innerHTML = '<param name="allowFullScreen" value="true"><param name="flashvars">'
<param name="allowFullScreen" value="true" /> obj.children[1].value = "hostname=www.twitch.tv&channel=#{channel}&auto_play=true&start_volume=25"
<param name="movie" value="http://www.twitch.tv/widgets/live_embed_player.swf" /> obj
<param name="flashvars" value="hostname=www.twitch.tv&channel=#{channel}&auto_play=true&start_volume=25" />
"""
, ,
key: 'Vocaroo' key: 'Vocaroo'
regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/ regExp: /.*(?:vocaroo.com\/)([^#\&\?]*).*/

View File

@ -44,10 +44,7 @@ CatalogLinks =
CatalogLinks.el.title = "Turn catalog links #{if useCatalog then 'off' else 'on'}." CatalogLinks.el.title = "Turn catalog links #{if useCatalog then 'off' else 'on'}."
external: (board) -> external: (board) ->
switch board if board in ['a', 'c', 'g', 'co', 'k', 'm', 'o', 'p', 'v', 'vg', 'w', 'cm', '3', 'adv', 'an', 'cgl', 'ck', 'diy', 'fa', 'fit', 'int', 'jp', 'mlp', 'lit', 'mu', 'n', 'po', 'sci', 'toy', 'trv', 'tv', 'vp', 'x', 'q']
when 'a', 'c', 'g', 'co', 'k', 'm', 'o', 'p', 'v', 'vg', 'w', 'cm', '3', 'adv', 'an', 'cgl', 'ck', 'diy', 'fa', 'fit', 'int', 'jp', 'mlp', 'lit', 'mu', 'n', 'po', 'sci', 'toy', 'trv', 'tv', 'vp', 'x', 'q' "http://catalog.neet.tv/#{board}"
"http://catalog.neet.tv/#{board}" else
when 'd', 'e', 'gif', 'h', 'hr', 'hc', 'r9k', 's', 'pol', 'soc', 'u', 'i', 'ic', 'hm', 'r', 'w', 'wg', 'wsg', 't', 'y' "/#{board}/catalog"
"http://4index.gropes.us/#{board}"
else
"/#{board}/catalog"

View File

@ -19,6 +19,7 @@ Keybinds =
keydown: (e) -> keydown: (e) ->
return unless key = Keybinds.keyCode e return unless key = Keybinds.keyCode e
{target} = e {target} = e
return if target.nodeName is 'EMBED' # Prevent keybinds from firing on /f/ embeds.
if target.nodeName in ['INPUT', 'TEXTAREA'] if target.nodeName in ['INPUT', 'TEXTAREA']
return unless /(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test key return unless /(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test key
unless g.VIEW is 'catalog' unless g.VIEW is 'catalog'

View File

@ -43,8 +43,7 @@ QR =
initReady: -> initReady: ->
$.off d, '4chanXInitFinished', @initReady $.off d, '4chanXInitFinished', @initReady
QR.postingIsEnabled = !!$.id 'postForm' QR.postingIsEnabled = !!$.id 'postForm'
unless QR.postingIsEnabled return unless QR.postingIsEnabled
return
$.on d, 'QRGetSelectedPost', ({detail: cb}) -> $.on d, 'QRGetSelectedPost', ({detail: cb}) ->
cb QR.selected cb QR.selected
@ -359,26 +358,23 @@ QR =
if /^text\//.test file.type if /^text\//.test file.type
if isSingle if isSingle
post = QR.selected post = QR.selected
else if index isnt 0 or (post = QR.posts[QR.posts.length - 1]).com else if (post = QR.posts[QR.posts.length - 1]).com
post = new QR.post() post = new QR.post()
post.pasteText file post.pasteText file
return return
unless file.type in QR.mimeTypes unless file.type in QR.mimeTypes
QR.error "#{file.name}: Unsupported file type." QR.error "#{file.name}: Unsupported file type."
return unless isSingle return
max = QR.nodes.fileInput.max max = QR.nodes.fileInput.max
max = Math.min(max, QR.max_size_video) if /^video\//.test file.type max = Math.min(max, QR.max_size_video) if /^video\//.test file.type
if file.size > max if file.size > max
QR.error "#{file.name}: File too large (file: #{$.bytesToString file.size}, max: #{$.bytesToString max})." QR.error "#{file.name}: File too large (file: #{$.bytesToString file.size}, max: #{$.bytesToString max})."
return unless isSingle return
if isSingle if isSingle
post = QR.selected post = QR.selected
else if index isnt 0 or (post = QR.posts[QR.posts.length - 1]).file else if (post = QR.posts[QR.posts.length - 1]).file
post = new QR.post() post = new QR.post()
if /^text/.test file.type post.setFile file
return post.pasteText file
else
post.setFile file
openFileInput: (e) -> openFileInput: (e) ->
e.stopPropagation() e.stopPropagation()
@ -436,7 +432,6 @@ QR =
setNode 'addPost', '#add-post' setNode 'addPost', '#add-post'
setNode 'charCount', '#char-count' setNode 'charCount', '#char-count'
setNode 'fileSubmit', '#file-n-submit' setNode 'fileSubmit', '#file-n-submit'
setNode 'filesize', '#qr-filesize'
setNode 'filename', '#qr-filename' setNode 'filename', '#qr-filename'
setNode 'fileContainer', '#qr-filename-container' setNode 'fileContainer', '#qr-filename-container'
setNode 'fileRM', '#qr-filerm' setNode 'fileRM', '#qr-filerm'

View File

@ -82,6 +82,7 @@ QR.post = class
(QR.posts[index-1] or QR.posts[index+1]).select() (QR.posts[index-1] or QR.posts[index+1]).select()
QR.posts.splice index, 1 QR.posts.splice index, 1
QR.status() QR.status()
delete: -> delete: ->
$.rm @nodes.el $.rm @nodes.el
URL.revokeObjectURL @URL URL.revokeObjectURL @URL
@ -185,7 +186,7 @@ QR.post = class
if errors.length if errors.length
QR.error error for error in errors QR.error error for error in errors
@URL = fileURL # this.removeFile will revoke this proper. @URL = fileURL # this.removeFile will revoke this proper.
return @rmFile() return if (QR.posts.length is 1) or (@com and @com.length) then @rmFile() else @rm() # I wrote this while listening to MCR
# Generate thumbnails only if they're really big. # Generate thumbnails only if they're really big.
# Resized pictures through canvases look like ass, # Resized pictures through canvases look like ass,