Merge branch 'master' of https://github.com/seaweedchan/4chan-x into v3
Conflicts: LICENSE builds/4chan-X.js builds/4chan-X.user.js builds/crx/script.js
18
CHANGELOG.md
@ -1,8 +1,24 @@
|
||||
seaweedchan:
|
||||
- Fix various embedding issues
|
||||
- Fix Link Title depending on Embedding
|
||||
- Added favicons to links that can be embedded
|
||||
- Add gist embedding
|
||||
|
||||
### 1.1.4 - 2013-04-29
|
||||
seaweedchan:
|
||||
- Change ESC functionality in QR to autohide if Persistent QR is enabled
|
||||
- Add /v/ and /vg/ archiving to archive.nihil-ad-rem.net, and make sure Archiver Selection settings actually switch to it
|
||||
- Add option to toggle between updater and stats fixed in header or floating
|
||||
|
||||
MayhemYDG:
|
||||
- Add nyafuu archiving for /w/
|
||||
- Add /d/ archive
|
||||
|
||||
### 1.1.3 - 2013-04-28
|
||||
seaweedchan:
|
||||
- Chrome doesn't get .null, so don't style it
|
||||
- Fix count when auto update is disabled and set updater text to "Update"
|
||||
- Remove /v/ and /vg/ redirection. See https://archive.foolz.us/foolz/thread/509388/ for news and how you can donate to bring /v/ and /vg/ archiving back.
|
||||
- Remove /v/ and /vg/ redirection from Foolz.
|
||||
- Toggle keybind for header auto-hiding
|
||||
|
||||
MayhemYDG:
|
||||
|
||||
@ -85,6 +85,7 @@ module.exports = (grunt) ->
|
||||
|
||||
concurrent:
|
||||
build: [
|
||||
'concat:license'
|
||||
'build-crx'
|
||||
'build-userjs'
|
||||
'build-userscript'
|
||||
@ -152,7 +153,6 @@ module.exports = (grunt) ->
|
||||
|
||||
grunt.registerTask 'build', [
|
||||
'concurrent:build'
|
||||
'concat:license'
|
||||
]
|
||||
|
||||
grunt.registerTask 'build-crx', [
|
||||
|
||||
2
LICENSE
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 4chan X - Version 1.1.3 - 2013-04-30
|
||||
* 4chan X - Version 1.1.4 - 2013-04-30
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// ==UserScript==
|
||||
// @name 4chan X
|
||||
// @version 1.1.3
|
||||
// @version 1.1.4
|
||||
// @namespace 4chan-X
|
||||
// @description Cross-browser userscript for maximum lurking on 4chan.
|
||||
// @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "4chan X",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"manifest_version": 2,
|
||||
"description": "Cross-browser userscript for maximum lurking on 4chan.",
|
||||
"icons": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "4chan-X",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"description": "Cross-browser userscript for maximum lurking on 4chan.",
|
||||
"meta": {
|
||||
"name": "4chan X",
|
||||
|
||||
@ -13,13 +13,19 @@ Redirect =
|
||||
"//archive.foolz.us/#{boardID}/full_image/#{filename}"
|
||||
when 'u'
|
||||
"//nsfw.foolz.us/#{boardID}/full_image/#{filename}"
|
||||
when 'v', 'vg'
|
||||
"//archive.nihil-ad-rem.net/#{boardID}/full_image/#{filename}"
|
||||
when 'po'
|
||||
"//archive.thedarkcave.org/#{boardID}/full_image/#{filename}"
|
||||
when 'hr', 'tv'
|
||||
"http://archive.4plebs.org/#{boardID}/full_image/#{filename}"
|
||||
when 'c', 'w', 'wg'
|
||||
"//archive.nyafuu.org/#{boardID}/full_image/#{filename}"
|
||||
when 'd'
|
||||
"//loveisover.me/#{boardID}/full_image/#{filename}"
|
||||
when 'ck', 'fa', 'lit', 's4s'
|
||||
"//fuuka.warosu.org/#{boardID}/full_image/#{filename}"
|
||||
when 'cgl', 'g', 'mu', 'w'
|
||||
when 'cgl', 'g', 'mu'
|
||||
"//rbt.asia/#{boardID}/full_image/#{filename}"
|
||||
when 'an', 'k', 'toy', 'x'
|
||||
"http://archive.heinessen.com/#{boardID}/full_image/#{filename}"
|
||||
@ -58,11 +64,18 @@ Redirect =
|
||||
else
|
||||
null)
|
||||
|
||||
unless archive.boards.contains g.BOARD.ID
|
||||
Conf['archivers'] = archive
|
||||
|
||||
archiver:
|
||||
'Foolz':
|
||||
base: 'https://archive.foolz.us'
|
||||
boards: ['a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'vp', 'vr', 'wsg']
|
||||
type: 'foolfuuka'
|
||||
'NihilAdRem':
|
||||
base: '//archive.nihil-ad-rem.net'
|
||||
boards: ['v', 'vg']
|
||||
type: 'foolfuuka'
|
||||
'NSFWFoolz':
|
||||
base: 'https://nsfw.foolz.us'
|
||||
boards: ['u']
|
||||
@ -75,6 +88,14 @@ Redirect =
|
||||
base: 'http://archive.4plebs.org'
|
||||
boards: ['hr', 'tg', 'tv', 'x']
|
||||
base: 'foolfuuka'
|
||||
'NyaFuu':
|
||||
base: '//archive.nyafuu.org'
|
||||
boards: ['c', 'w', 'wg']
|
||||
type: 'foolfuuka'
|
||||
'LoveIsOver':
|
||||
base: '//loveisover.me'
|
||||
boards: ['d']
|
||||
type: 'foolfuuka'
|
||||
'Warosu':
|
||||
base: '//fuuka.warosu.org'
|
||||
boards: ['cgl', 'ck', 'fa', 'jp', 'lit', 's4s', 'q', 'tg']
|
||||
@ -95,10 +116,6 @@ Redirect =
|
||||
base: '//www.cliché.net/4chan/cgi-board.pl'
|
||||
boards: ['e']
|
||||
type: 'fuuka'
|
||||
'NyaFuu':
|
||||
base: '//archive.nyafuu.org'
|
||||
boards: ['c', 'w']
|
||||
type: 'fuuka'
|
||||
|
||||
path: (base, archiver, data) ->
|
||||
if data.isSearch
|
||||
|
||||
@ -217,6 +217,10 @@ Config =
|
||||
true
|
||||
'Display reply and image count.'
|
||||
]
|
||||
'Updater and Stats in Header': [
|
||||
true,
|
||||
'Places the thread updater and thread stats in the header instead of floating them.'
|
||||
]
|
||||
'Thread Watcher': [
|
||||
true
|
||||
'Bookmark threads.'
|
||||
|
||||
@ -139,22 +139,22 @@ Header =
|
||||
for a in as
|
||||
if a.textContent is board
|
||||
a = a.cloneNode true
|
||||
if /-title/.test t
|
||||
a.textContent = a.title
|
||||
else if /-replace/.test t
|
||||
if $.hasClass a, 'current'
|
||||
a.textContent = a.title
|
||||
|
||||
a.textContent = if /-title/.test(t) or /-replace/.test(t) and $.hasClass a, 'current'
|
||||
a.title
|
||||
else if /-full/.test t
|
||||
a.textContent = "/#{board}/ - #{a.title}"
|
||||
else if /-(index|catalog|text)/.test t
|
||||
if m = t.match /-(index|catalog)/
|
||||
a.setAttribute 'data-only', m[1]
|
||||
a.href = "//boards.4chan.org/#{board}/"
|
||||
a.href += 'catalog' if m[1] is 'catalog'
|
||||
if m = t.match /-text:"(.+)"/
|
||||
a.textContent = m[1]
|
||||
else if board is '@'
|
||||
$.addClass a, 'navSmall'
|
||||
"/#{board}/ - #{a.title}"
|
||||
else if m = t.match /-text:"(.+)"/
|
||||
m[1]
|
||||
else
|
||||
a.textContent
|
||||
|
||||
if m = t.match /-(index|catalog)/
|
||||
a.setAttribute 'data-only', m[1]
|
||||
a.href = "//boards.4chan.org/#{board}/"
|
||||
a.href += 'catalog' if m[1] is 'catalog'
|
||||
|
||||
$.addClass a, 'navSmall' if board is '@'
|
||||
return a
|
||||
$.tn t
|
||||
$.add list, nodes
|
||||
|
||||
@ -66,6 +66,8 @@ a[href="javascript:;"] {
|
||||
#qp, #ihover,
|
||||
#navlinks, .fixed #header-bar,
|
||||
#watcher,
|
||||
:root.float #updater,
|
||||
:root.float #thread-stats,
|
||||
#qr {
|
||||
position: fixed;
|
||||
}
|
||||
@ -374,9 +376,6 @@ a[href="javascript:;"] {
|
||||
a.hide-announcement {
|
||||
float: left;
|
||||
}
|
||||
#toggleMsgBtn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Unread */
|
||||
#unread-line {
|
||||
@ -385,13 +384,14 @@ a.hide-announcement {
|
||||
}
|
||||
|
||||
/* Thread Updater */
|
||||
#updater:not(:hover) {
|
||||
#updater {
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
#updater > .move {
|
||||
padding: 0 3px;
|
||||
padding: 5px 3px 0px;
|
||||
margin-bottom: -3px;
|
||||
}
|
||||
#updater > div:last-child {
|
||||
text-align: center;
|
||||
@ -399,11 +399,8 @@ a.hide-announcement {
|
||||
#updater input[type=number] {
|
||||
width: 4em;
|
||||
}
|
||||
#updater:not(:hover) > div:not(.move) {
|
||||
display: none;
|
||||
}
|
||||
#updater input[type="button"] {
|
||||
width: 100%;
|
||||
:root.float #updater {
|
||||
padding: 0px 3px;
|
||||
}
|
||||
.new {
|
||||
color: limegreen;
|
||||
@ -444,6 +441,12 @@ a.hide-announcement {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
:root.float #post-count, :root.float #file-count {
|
||||
pointer-events: none;
|
||||
}
|
||||
:root.float #thread-stats {
|
||||
padding: 0px 3px;
|
||||
}
|
||||
|
||||
/* Quote */
|
||||
.deadlink {
|
||||
@ -892,3 +895,36 @@ a:only-of-type > .remove {
|
||||
cursor: pointer;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
/* Link Title Favicons */
|
||||
.linkify.YouTube {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/youtube.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.Vimeo {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/vimeo.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.SoundCloud {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/soundcloud.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.audio {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/audio.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.LiveLeak {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/liveleak.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.Vocaroo {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/vocaroo.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.pastebin {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/pastebin.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.gist {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/gist.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
BIN
src/General/img/links/audio.png
Normal file
|
After Width: | Height: | Size: 700 B |
BIN
src/General/img/links/gist.png
Normal file
|
After Width: | Height: | Size: 683 B |
BIN
src/General/img/links/liveleak.png
Normal file
|
After Width: | Height: | Size: 952 B |
BIN
src/General/img/links/pastebin.png
Normal file
|
After Width: | Height: | Size: 871 B |
BIN
src/General/img/links/soundcloud.png
Normal file
|
After Width: | Height: | Size: 491 B |
BIN
src/General/img/links/vimeo.png
Normal file
|
After Width: | Height: | Size: 435 B |
BIN
src/General/img/links/vocaroo.png
Normal file
|
After Width: | Height: | Size: 928 B |
BIN
src/General/img/links/youtube.png
Normal file
|
After Width: | Height: | Size: 347 B |
@ -373,7 +373,7 @@ $.set = do ->
|
||||
items = {}
|
||||
localItems = {}
|
||||
catch err
|
||||
c.error err
|
||||
c.error err.stack
|
||||
|
||||
(key, val) ->
|
||||
if typeof key is 'string'
|
||||
|
||||
@ -125,21 +125,17 @@ Linkify =
|
||||
textContent: @getAttribute("data-title") or url
|
||||
|
||||
@textContent = '(embed)'
|
||||
$.addClass el, "#{@getAttribute 'data-service'}"
|
||||
|
||||
# Embed
|
||||
else
|
||||
# We create an element to embed
|
||||
el = (type = Linkify.types[@getAttribute("data-service")]).el.call @
|
||||
|
||||
# Set style values.
|
||||
if style = type.style
|
||||
el.style.cssText = style
|
||||
el.style.cssText = if style = type.style
|
||||
style
|
||||
else
|
||||
items =
|
||||
'embedWidth': Config['embedWidth']
|
||||
'embedHeight': Config['embedHeight']
|
||||
$.get items, (items) ->
|
||||
el.style.cssText = "border: 0; width: #{items['embedWidth']}px; height: #{items['embedHeight']}px"
|
||||
"border: 0; width: 640px; height: 390px"
|
||||
|
||||
@textContent = '(unembed)'
|
||||
|
||||
@ -187,32 +183,44 @@ Linkify =
|
||||
src: @name
|
||||
|
||||
SoundCloud:
|
||||
regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/
|
||||
regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/
|
||||
style: 'height: auto; width: 500px; display: inline-block;'
|
||||
el: ->
|
||||
div = $.el 'div',
|
||||
className: "soundcloud"
|
||||
name: "soundcloud"
|
||||
name: "soundcloud"
|
||||
$.ajax(
|
||||
"//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=#{@getAttribute 'data-originalURL'}&color=#{Style.colorToHex Themes[Conf['theme']]['Background Color']}"
|
||||
"//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{@name}"
|
||||
div: div
|
||||
onloadend: ->
|
||||
@div.innerHTML = JSON.parse(@responseText).html
|
||||
false)
|
||||
div
|
||||
title:
|
||||
api: -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{@name}"
|
||||
text: -> JSON.parse(@responseText).title
|
||||
|
||||
pastebin:
|
||||
regExp: /.*(?:pastebin.com\/)([^#\&\?]*).*/
|
||||
regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/
|
||||
el: ->
|
||||
div = $.el 'iframe',
|
||||
src: "http://pastebin.com/embed_iframe.php?i=#{@name}"
|
||||
|
||||
gist:
|
||||
regExp: /.*(?:gist.github.com\/.*\/)([^#\&\?]*).*/
|
||||
el: ->
|
||||
div = $.el 'iframe',
|
||||
# Github doesn't allow embedding straight from the site, so we use an external site to bypass that.
|
||||
src: "http://www.purplegene.com/script?url=https://gist.github.com/#{@name}.js"
|
||||
|
||||
embedder: (a) ->
|
||||
return [a] unless Conf['Embedding']
|
||||
return [a] unless Conf['Link Title']
|
||||
titles = {}
|
||||
|
||||
callbacks = ->
|
||||
a.textContent = switch @status
|
||||
when 200, 304
|
||||
title = "[#{embed.getAttribute 'data-service'}] #{service.text.call @}"
|
||||
title = "#{service.text.call @}"
|
||||
embed.setAttribute 'data-title', title
|
||||
titles[embed.name] = [title, Date.now()]
|
||||
$.set 'CachedTitles', titles
|
||||
@ -235,13 +243,16 @@ Linkify =
|
||||
|
||||
embed.setAttribute 'data-service', key
|
||||
embed.setAttribute 'data-originalURL', a.href
|
||||
$.addClass a, "#{embed.getAttribute 'data-service'}"
|
||||
|
||||
$.on embed, 'click', Linkify.toggle
|
||||
|
||||
unless Conf['Embedding']
|
||||
embed.hidden = true
|
||||
|
||||
if Conf['Link Title'] and (service = type.title)
|
||||
$.get 'CachedTitles', {}, (item) ->
|
||||
titles = item['CachedTitles']
|
||||
|
||||
if title = titles[match[1]]
|
||||
a.textContent = title[0]
|
||||
embed.setAttribute 'data-title', title[0]
|
||||
|
||||
@ -11,7 +11,7 @@ ExpandComment =
|
||||
name: 'Comment Expansion'
|
||||
cb: @node
|
||||
node: ->
|
||||
if a = $ '.abbr > a', @nodes.comment
|
||||
if a = $ '.abbr > a:not([onclick])', @nodes.comment
|
||||
$.on a, 'click', ExpandComment.cb
|
||||
callbacks: []
|
||||
cb: (e) ->
|
||||
@ -63,4 +63,4 @@ ExpandComment =
|
||||
|
||||
for callback in ExpandComment.callbacks
|
||||
callback.call post
|
||||
return
|
||||
return
|
||||
|
||||
@ -41,7 +41,10 @@ Keybinds =
|
||||
for notification in notifications
|
||||
$('.close', notification).click()
|
||||
else if QR.nodes
|
||||
QR.close()
|
||||
if Conf['Persistent QR']
|
||||
QR.hide()
|
||||
else
|
||||
QR.close()
|
||||
when Conf['Spoiler tags']
|
||||
return if target.nodeName isnt 'TEXTAREA'
|
||||
Keybinds.tags 'spoiler', target
|
||||
|
||||
@ -1,14 +1,20 @@
|
||||
ThreadStats =
|
||||
init: ->
|
||||
return if g.VIEW isnt 'thread' or !Conf['Thread Stats']
|
||||
@dialog = sc = $.el 'span',
|
||||
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span></div>"
|
||||
id: 'thread-stats'
|
||||
|
||||
if Conf['Updater and Stats in Header']
|
||||
@dialog = sc = $.el 'span',
|
||||
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>"
|
||||
id: 'thread-stats'
|
||||
Header.addShortcut sc
|
||||
else
|
||||
@dialog = sc = UI.dialog 'thread-stats', 'bottom: 0px; right: 0px;',
|
||||
"<div class=move><span id=post-count>0</span> / <span id=file-count>0</span></div>"
|
||||
$.ready =>
|
||||
$.add d.body, sc
|
||||
|
||||
@postCountEl = $ '#post-count', sc
|
||||
@fileCountEl = $ '#file-count', sc
|
||||
|
||||
Header.addShortcut sc
|
||||
|
||||
Thread::callbacks.push
|
||||
name: 'Thread Stats'
|
||||
|
||||
@ -3,20 +3,28 @@ ThreadUpdater =
|
||||
return if g.VIEW isnt 'thread' or !Conf['Thread Updater']
|
||||
|
||||
checked = if Conf['Auto Update'] then 'checked' else ''
|
||||
@dialog = sc = $.el 'span',
|
||||
innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>"
|
||||
id: 'updater'
|
||||
|
||||
@timer = $ '#update-timer', sc
|
||||
if Conf['Updater and Stats in Header']
|
||||
@dialog = sc = $.el 'span',
|
||||
innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>"
|
||||
id: 'updater'
|
||||
Header.addShortcut sc
|
||||
else
|
||||
@dialog = sc = UI.dialog 'updater', 'bottom: 0px; left: 0px;',
|
||||
"<div class=move></div><span id=update-status></span><span id=update-timer title='Update now'></span>"
|
||||
$.addClass doc, 'float'
|
||||
$.ready =>
|
||||
$.addClass doc, 'float'
|
||||
$.add d.body, sc
|
||||
|
||||
@checkPostCount = 0
|
||||
|
||||
@timer = $ '#update-timer', sc
|
||||
@status = $ '#update-status', sc
|
||||
|
||||
$.on @timer, 'click', ThreadUpdater.update
|
||||
$.on @status, 'click', ThreadUpdater.update
|
||||
|
||||
@checkPostCount = 0
|
||||
|
||||
Header.addShortcut sc
|
||||
|
||||
subEntries = []
|
||||
for name, conf of Config.updater.checkbox
|
||||
checked = if Conf[name] then 'checked' else ''
|
||||
|
||||
@ -39,6 +39,7 @@ ThreadWatcher =
|
||||
for id, props of watched[board]
|
||||
x = $.el 'a',
|
||||
textContent: '×'
|
||||
className: 'close'
|
||||
href: 'javascript:;'
|
||||
$.on x, 'click', ThreadWatcher.cb.x
|
||||
link = $.el 'a', props
|
||||
|
||||