Start merging ccd0's 4chan X stuff

This commit is contained in:
Zixaphir 2015-01-08 13:53:38 -07:00
parent 5449031e60
commit 6f84ae9abe
13 changed files with 4739 additions and 4571 deletions

View File

@ -1,7 +1,8 @@
module.exports = (grunt) -> module.exports = (grunt) ->
grunt.util.linefeed = '\n'
importHTML = (filename) -> importHTML = (filename) ->
"\"\"\"#{grunt.file.read("src/General/html/#{filename}.html").replace(/^\s+|\s+$</gm, '').replace(/\n/g, '')}\"\"\"" "(innerHTML: #{JSON.stringify grunt.file.read("src/General/html/#{filename}.html").replace(/^\s+|\s+$</gm, '').replace(/\n/g, '')})"
html = (template) -> html = (template) ->
parts = template.split /([\$&@]){([^}`]*)}/ parts = template.split /([\$&@]){([^}`]*)}/
@ -38,6 +39,8 @@ module.exports = (grunt) ->
pkg = grunt.config 'pkg' pkg = grunt.config 'pkg'
pkg.importHTML = importHTML pkg.importHTML = importHTML
pkg.html = html pkg.html = html
pkg.assert = assert
pkg.tests_enabled or= false
pkg pkg
enumerable: true enumerable: true
) )
@ -52,17 +55,18 @@ module.exports = (grunt) ->
'src/General/Build.coffee' 'src/General/Build.coffee'
'src/General/Get.coffee' 'src/General/Get.coffee'
'src/General/UI.coffee' 'src/General/UI.coffee'
'src/Filtering/*' 'src/General/CrossOrigin.coffee'
'src/Quotelinks/*' 'src/Filtering/**/*.coffee'
'src/Linkification/*' 'src/Quotelinks/**/*.coffee'
'src/Posting/QR.coffee' 'src/Posting/Captcha.coffee'
'src/Posting/*' 'src/Posting/**/*.coffee'
'src/Images/*' 'src/Images/**/*.coffee'
'src/Menu/*' 'src/Linkification/**/*.coffee'
'src/Monitoring/*' 'src/Menu/**/*.coffee'
'src/Archive/*' 'src/Monitoring/**/*.coffee'
'src/Theming/*' 'src/Archive/**/*.coffee'
'src/Miscellaneous/*' 'src/Miscellaneous/**/*.coffee'
'src/Theming/**/*.coffee'
'src/General/Navigate.coffee' 'src/General/Navigate.coffee'
'src/General/Settings.coffee' 'src/General/Settings.coffee'
'src/General/Main.coffee' 'src/General/Main.coffee'

View File

@ -1,23 +0,0 @@
// ==UserScript==
// @name 4chan X
// @version 1.7.33
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
// @description Cross-browser userscript for maximum lurking on 4chan.
// @license MIT; https://github.com/ccd0/4chan-x/blob/master/LICENSE
// @match *://boards.4chan.org/*
// @match *://sys.4chan.org/*
// @match *://a.4cdn.org/*
// @match *://i.4cdn.org/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_listValues
// @grant GM_openInTab
// @grant GM_xmlhttpRequest
// @run-at document-start
// @updateURL https://ccd0.github.io/4chan-x/builds/4chan-X.meta.js
// @downloadURL https://ccd0.github.io/4chan-x/builds/4chan-X.user.js
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAF5JREFUeNrtkTESABAQxPD/R6tsE2dUGYUtFJvLDKf93KevHJAjpBorAQWSBIKqFASC4G0pCAkm4GfaEvgYXl0T6HBaE97f0vmnfYHbZOMLZCx9ISdKWwjOWZSC8GYm4SUGwfYgqI4AAAAASUVORK5CYII=
// ==/UserScript==

View File

@ -13,7 +13,6 @@
// @match *://i.4cdn.org/* // @match *://i.4cdn.org/*
// @exclude *://blog.4chan.org/* // @exclude *://blog.4chan.org/*
// @exclude *://dis.4chan.org/* // @exclude *://dis.4chan.org/*
// @exclude *://a.4cdn.org/*
// @grant GM_getValue // @grant GM_getValue
// @grant GM_setValue // @grant GM_setValue
// @grant GM_deleteValue // @grant GM_deleteValue

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -15,7 +15,7 @@
"run_at": "document_start" "run_at": "document_start"
}], }],
"homepage_url": "http://zixaphir.github.com/appchan-x/", "homepage_url": "http://zixaphir.github.com/appchan-x/",
"minimum_chrome_version": "33", "minimum_chrome_version": "32",
"permissions": [ "permissions": [
"storage", "storage",
"http://*/", "http://*/",

File diff suppressed because one or more lines are too long

View File

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

View File

@ -19,15 +19,14 @@
], ],
"excludes": [ "excludes": [
"*://blog.4chan.org/*", "*://blog.4chan.org/*",
"*://dis.4chan.org/*", "*://dis.4chan.org/*"
"*://a.4cdn.org/*"
], ],
"files": { "files": {
"metajs": "appchan-x.meta.js", "metajs": "appchan-x.meta.js",
"userjs": "appchan-x.user.js" "userjs": "appchan-x.user.js"
}, },
"min": { "min": {
"chrome": "33", "chrome": "32",
"firefox": "26", "firefox": "26",
"greasemonkey": "1.14" "greasemonkey": "1.14"
} }
@ -53,7 +52,9 @@
"author": "Zixaphir <zixaphirmoxphar@gmail.com>", "author": "Zixaphir <zixaphirmoxphar@gmail.com>",
"contributors": [ "contributors": [
"Nicolas Stepien <stepien.nicolas@gmail.com>", "Nicolas Stepien <stepien.nicolas@gmail.com>",
"James Campos <james.r.campos@gmail.com>" "James Campos <james.r.campos@gmail.com>",
"seaweedchan <jtbates@asu.edu>",
"ccd0"
], ],
"license": "MIT", "license": "MIT",
"readmeFilename": "README.md", "readmeFilename": "README.md",

View File

@ -53,7 +53,10 @@ Redirect =
post: (archive, {boardID, postID}) -> post: (archive, {boardID, postID}) ->
# For fuuka-based archives: # For fuuka-based archives:
# https://github.com/eksopl/fuuka/issues/27 # https://github.com/eksopl/fuuka/issues/27
URL = new String "#{Redirect.protocol archive}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}" protocol = Redirect.protocol archive
URL = new String "#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}"
return '' unless Redirect.securityCheck URL
URL.archive = archive URL.archive = archive
URL URL
@ -76,3 +79,17 @@ Redirect =
else else
"#{boardID}/?task=search2&search_#{if type is 'image' then 'media_hash' else type}=#{value}" "#{boardID}/?task=search2&search_#{if type is 'image' then 'media_hash' else type}=#{value}"
"#{Redirect.protocol archive}#{archive.domain}/#{path}" "#{Redirect.protocol archive}#{archive.domain}/#{path}"
securityCheck: (URL) ->
/^https:\/\//.test(URL) or
location.protocol is 'http:' or
Conf['Except Archives from Encryption']
navigate: (URL, alternative) ->
if URL and (
Redirect.securityCheck(URL) or
confirm "Redirect to #{URL}?\n\nYour connection will not be encrypted."
)
location.replace URL
else if alternative
location.replace alternative

View File

@ -5,7 +5,7 @@
"http": false, "http": false,
"https": true, "https": true,
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["a", "biz", "c", "co", "diy", "gd", "h", "i", "int", "jp", "k", "m", "mlp", "out", "po", "r9k", "s4s", "sci", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], "boards": ["a", "biz", "c", "co", "diy", "gd", "h", "i", "int", "jp", "k", "m", "mlp", "out", "po", "r9k", "s4s", "sci", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
"files": ["a", "biz", "c", "co", "diy", "gd", "h", "i", "jp", "k", "m", "mlp", "po", "s4s", "sci", "tg", "u", "v", "vg", "vp", "vr", "wsg"] "files": ["a", "biz", "c", "co", "diy", "gd", "h", "i", "jp", "k", "m", "mlp", "po", "s4s", "sci", "tg", "u", "v", "vg", "vp", "vr", "wsg"]
}, { }, {
"uid": 3, "uid": 3,
@ -53,12 +53,13 @@
"boards": ["asp", "cm", "h", "hc", "hm", "n", "p", "r", "s", "soc", "y"], "boards": ["asp", "cm", "h", "hc", "hm", "n", "p", "r", "s", "soc", "y"],
"files": ["asp", "cm", "h", "hc", "hm", "n", "p", "r", "s", "soc", "y"] "files": ["asp", "cm", "h", "hc", "hm", "n", "p", "r", "s", "soc", "y"]
}, { }, {
"uid": 17, "uid": 21,
"name": "imcute", "name": "imcute",
"domain": "imcute.yt", "domain": "imcute.yt",
"http": true, "http": true,
"https": false, "https": false,
"software": "foolfuuka", "software": "foolfuuka",
"boards": ["an", "fit", "gif", "int", "mlp", "out", "r9k", "toy"], "boards": ["an", "fit", "gif", "int", "mlp", "out", "r9k", "toy"],
"files": ["an", "fit", "gif", "int", "mlp", "out", "r9k", "toy"] "files": ["an", "fit", "gif", "int", "mlp", "out", "r9k", "toy"],
"imagehosts": ["http://imcute.yt/"]
}] }]

View File

@ -1,10 +1,12 @@
Anonymize = Anonymize =
init: -> init: ->
return if !Conf['Anonymize'] return unless g.VIEW in ['index', 'thread', 'archive'] and Conf['Anonymize']
return @archive() if g.VIEW is 'archive'
Post.callbacks.push Post.callbacks.push
name: 'Anonymize' name: 'Anonymize'
cb: @node cb: @node
node: -> node: ->
return if @info.capcode or @isClone return if @info.capcode or @isClone
{name, tripcode, email} = @nodes {name, tripcode, email} = @nodes
@ -16,3 +18,8 @@ Anonymize =
if @info.email if @info.email
$.replace email, name $.replace email, name
delete @nodes.email delete @nodes.email
archive: ->
$.ready ->
name.textContent = 'Anonymous' for name in $$ '.name'
$.rm trip for trip in $$ '.postertrip'

View File

@ -1,21 +1,21 @@
Filter = Filter =
filters: {} filters: {}
init: -> init: ->
return if !Conf['Filter'] return unless g.VIEW in ['index', 'thread'] and Conf['Filter']
unless Conf['Filtered Backlinks'] unless Conf['Filtered Backlinks']
$.addClass doc, 'hide-backlinks' $.addClass doc, 'hide-backlinks'
for key of Config.filter for key of Config.filter
@filters[key] = [] @filters[key] = []
for filter in Conf[key].split '\n' for line in Conf[key].split '\n'
continue if filter[0] is '#' continue if line[0] is '#'
unless regexp = filter.match /\/(.+)\/(\w*)/ unless regexp = line.match /\/(.+)\/(\w*)/
continue continue
# Don't mix up filter flags with the regular expression. # Don't mix up filter flags with the regular expression.
filter = filter.replace regexp[0], '' filter = line.replace regexp[0], ''
# Do not add this filter to the list if it's not a global one # Do not add this filter to the list if it's not a global one
# and it's not specifically applicable to the current board. # and it's not specifically applicable to the current board.
@ -33,7 +33,11 @@ Filter =
regexp = RegExp regexp[1], regexp[2] regexp = RegExp regexp[1], regexp[2]
catch err catch err
# I warned you, bro. # I warned you, bro.
new Notice 'warning', err.message, 60 new Notice 'warning', [
$.tn "Invalid #{key} filter: " + line,
$.el 'br'
$.tn err.message
], 60
continue continue
# Filter OPs along with their threads, replies only, or both. # Filter OPs along with their threads, replies only, or both.
@ -60,18 +64,7 @@ Filter =
top = filter.match(/top:(yes|no)/)?[1] or 'yes' top = filter.match(/top:(yes|no)/)?[1] or 'yes'
top = top is 'yes' # Turn it into a boolean top = top is 'yes' # Turn it into a boolean
@filters[key].push { @filters[key].push @createFilter regexp, op, stub, hl, top
hide: !hl
op: op
stub: stub
class: hl
top: top
match: regexp
test: if typeof regexp is 'string'
Filter.stringTest # MD5 checking
else
Filter.regexpTest
}
# Only execute filter types that contain valid filters. # Only execute filter types that contain valid filters.
unless @filters[key].length unless @filters[key].length
@ -82,25 +75,41 @@ Filter =
name: 'Filter' name: 'Filter'
cb: @node cb: @node
createFilter: (regexp, op, stub, hl, top) ->
test =
if typeof regexp is 'string'
# MD5 checking
Filter.stringTest
else
Filter.regexpTest
settings =
hide: !hl
stub: stub
class: hl
top: top
(value, isReply) -> return settings if Filter.test(test, value, isReply)
node: -> node: ->
return if @isClone return if @isClone or @isFetchedQuote
for key of Filter.filters for key of Filter.filters
value = Filter[key] @ value = Filter[key] @
# Continue if there's nothing to filter (no tripcode for example). # Continue if there's nothing to filter (no tripcode for example).
continue if value is false continue if value is false
for obj in Filter.filters[key] for filter in Filter.filters[key]
unless Filter.test obj, value, @isReply unless result = filter value, @isReply
continue continue
# Hide # Hide
if obj.hide if result.hide
continue unless @isReply or g.VIEW is 'index' continue unless @isReply or g.VIEW is 'index'
@hide "Hidden by filtering the #{key}: #{obj.match}", obj.stub @hide "Hidden by filtering the #{key}: #{result.match}", result.stub
return return
# Highlight # Highlight
@highlight "Highlighted by filtering the #{key}: #{obj.match}", obj.class, obj.top @highlight "Highlighted by filtering the #{key}: #{result.match}", result.class, result.top
stringTest: (string, value) -> stringTest: (string, value) ->
string is value string is value
@ -112,6 +121,7 @@ Filter =
unless test match, value unless test match, value
return false return false
true true
name: (post) -> name: (post) ->
if 'name' of post.info if 'name' of post.info
return post.info.name return post.info.name
@ -128,10 +138,6 @@ Filter =
if 'capcode' of post.info if 'capcode' of post.info
return post.info.capcode return post.info.capcode
false false
email: (post) ->
if 'email' of post.info
return post.info.email
false
subject: (post) -> subject: (post) ->
if 'subject' of post.info if 'subject' of post.info
return post.info.subject or false return post.info.subject or false
@ -151,7 +157,7 @@ Filter =
dimensions: (post) -> dimensions: (post) ->
{file} = post {file} = post
if file and (file.isImage or file.isVideo) if file and (file.isImage or file.isVideo)
return post.file.dimensions return file.dimensions
false false
filesize: (post) -> filesize: (post) ->
if post.file if post.file
@ -164,7 +170,7 @@ Filter =
menu: menu:
init: -> init: ->
return if !Conf['Menu'] or !Conf['Filter'] return unless g.VIEW in ['index', 'thread'] and Conf['Menu'] and Conf['Filter']
div = $.el 'div', div = $.el 'div',
textContent: 'Filter' textContent: 'Filter'
@ -182,7 +188,6 @@ Filter =
['Unique ID', 'uniqueID'] ['Unique ID', 'uniqueID']
['Tripcode', 'tripcode'] ['Tripcode', 'tripcode']
['Capcode', 'capcode'] ['Capcode', 'capcode']
['E-mail', 'email']
['Subject', 'subject'] ['Subject', 'subject']
['Comment', 'comment'] ['Comment', 'comment']
['Flag', 'flag'] ['Flag', 'flag']