Merge Zixaphir X

This commit is contained in:
seaweedchan 2013-08-12 00:07:39 -07:00
commit 875f0cd4f8
176 changed files with 3788 additions and 3166 deletions

3
.gitignore vendored
View File

@ -2,8 +2,7 @@ node_modules/
*~
*.db
tmp-crx/
tmp-userjs/
tmp-userscript/
builds/4chan-X-Chrome.zip
builds/4chan-X-Opera.nex
Gruntfile.js
Gruntfile.js

21
CHANGELOG.md Normal file → Executable file
View File

@ -1,3 +1,23 @@
**MayhemYDG**:
- **New feature**: `Show Dice Roll` (with @carboncopy)
- Shows dice that were entered into the email field on /tg/.
- Fix impossibility to create new threads when in dead threads.
- Fix flag filtering on /sp/ and /int/.
- Update archives. (with @woxxy and @proplex)
- Minor fixes.
- Minor optimizations.
**Zixaphir**:
- Linkifier Rewrite.
- Fix Quote Threading toggle.
- Added Twitch.tv and Vine embedding (with @ihavenoface)
- Keybinds to scroll to posts that quote you.
- Minor optimizations.
- Minor fixes.
**aeosynth**:
- Update Gruntfile.coffee.
### v1.2.25
*2013-08-04*
@ -54,7 +74,6 @@
### v1.2.17
*2013-06-17*
**seaweedchan**:
- Fix full images being forced onto their own line

138
Gruntfile.coffee Normal file → Executable file
View File

@ -1,18 +1,18 @@
module.exports = (grunt) ->
pkg = grunt.file.readJSON 'package.json'
concatOptions =
process:
data: pkg
process: Object.create(null, data:
get: -> grunt.config 'pkg'
enumerable: true
)
shellOptions =
stdout: true
stderr: true
stdout: true
stderr: true
failOnError: true
# Project configuration.
grunt.initConfig
pkg: pkg
pkg: grunt.file.readJSON 'package.json'
concat:
coffee:
options: concatOptions
@ -41,8 +41,8 @@ module.exports = (grunt) ->
meta:
options: concatOptions
files:
'LICENSE': 'src/General/meta/banner.js',
'latest.js': 'src/General/meta/latest.js'
'LICENSE': 'src/General/meta/banner.js',
'latest.js': 'src/General/meta/latest.js'
crx:
options: concatOptions
@ -51,19 +51,9 @@ module.exports = (grunt) ->
'builds/crx/script.js': [
'src/General/meta/botproc.js'
'src/General/meta/banner.js'
'src/General/meta/usestrict.js'
'tmp-<%= pkg.type %>/script.js'
]
userjs:
options: concatOptions
src: [
'src/General/meta/botproc.js'
'src/General/meta/metadata.js'
'src/General/meta/banner.js'
'tmp-<%= pkg.type %>/script.js'
]
dest: 'builds/<%= pkg.name %>.js'
userscript:
options: concatOptions
files:
@ -72,13 +62,13 @@ module.exports = (grunt) ->
'src/General/meta/botproc.js'
'src/General/meta/metadata.js'
'src/General/meta/banner.js'
'src/General/meta/usestrict.js'
'tmp-<%= pkg.type %>/script.js'
]
copy:
crx:
src: 'src/General/img/*.png'
dest: 'builds/crx/'
src: 'src/General/img/*.png'
dest: 'builds/crx/'
expand: true
flatten: true
@ -91,24 +81,31 @@ module.exports = (grunt) ->
build: [
'concat:meta'
'build-crx'
'build-userjs'
'build-userscript'
]
bump:
options:
updateConfigs: [
'pkg'
]
commit: false
createTag: false
push: false
shell:
commit:
options: shellOptions
command: [
'git checkout <%= pkg.meta.mainBranch %>',
'git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>."',
'git tag -a <%= pkg.version %> -m "<%= pkg.meta.name %> v<%= pkg.version %>."',
'git checkout <%= pkg.meta.mainBranch %>'
'git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>."'
'git tag -a <%= pkg.version %> -m "<%= pkg.meta.name %> v<%= pkg.version %>."'
'git tag -af stable -m "<%= pkg.meta.name %> v<%= pkg.version %>."'
].join(' && ')
stdout: true
].join ' && '
push:
options: shellOptions
command: 'git push origin --tags -f && git push origin --all'
command: 'git push origin --tags -f && git push origin --all'
watch:
all:
@ -124,35 +121,28 @@ module.exports = (grunt) ->
compress:
crx:
options:
archive: 'builds/4chan-X-Chrome.zip'
archive: 'builds/<%= pkg.name %>.zip'
level: 9
pretty: true
expand: true
cwd: 'builds/crx/'
src: '**'
expand: true
flatten: true
src: 'builds/crx/*'
dest: '/'
clean:
builds: 'builds'
tmpcrx: 'tmp-crx'
tmpuserjs: 'tmp-userjs'
builds: 'builds'
tmpcrx: 'tmp-crx'
tmpuserscript: 'tmp-userscript'
grunt.loadNpmTasks 'grunt-bump'
grunt.loadNpmTasks 'grunt-concurrent'
grunt.loadNpmTasks 'grunt-contrib-clean'
grunt.loadNpmTasks 'grunt-contrib-coffee'
grunt.loadNpmTasks 'grunt-contrib-compress'
grunt.loadNpmTasks 'grunt-contrib-concat'
grunt.loadNpmTasks 'grunt-contrib-copy'
grunt.loadNpmTasks 'grunt-contrib-watch'
grunt.loadNpmTasks 'grunt-shell'
require('matchdep').filterDev('grunt-*').forEach grunt.loadNpmTasks
grunt.registerTask 'default', [
'build'
]
grunt.registerTask 'set-build', 'Set the build type variable', (type) ->
pkg.type = type;
pkg = grunt.config 'pkg'
pkg.type = type
grunt.config 'pkg', pkg
grunt.log.ok 'pkg.type = %s', type
grunt.registerTask 'build', [
@ -168,14 +158,6 @@ module.exports = (grunt) ->
'clean:tmpcrx'
]
grunt.registerTask 'build-userjs', [
'set-build:userjs'
'concat:coffee'
'coffee:script'
'concat:userjs'
'clean:tmpuserjs'
]
grunt.registerTask 'build-userscript', [
'set-build:userscript'
'concat:coffee'
@ -185,40 +167,36 @@ module.exports = (grunt) ->
]
grunt.registerTask 'release', [
'default'
'compress:crx'
'build'
'shell:commit'
'shell:push'
'build-crx'
'compress:crx'
]
grunt.registerTask 'patch', [
'bump-only'
'reloadPkg'
grunt.registerTask 'patch', [
'bump'
'updcl:3'
'release'
]
grunt.registerTask 'minor', [
'bump-only:minor'
'reloadPkg'
grunt.registerTask 'minor', [
'bump:minor'
'updcl:2'
'release'
]
grunt.registerTask 'major', [
'bump-only:major'
'reloadPkg'
grunt.registerTask 'major', [
'bump:major'
'updcl:1'
'release'
]
grunt.registerTask 'reloadPkg', 'Reload the package', ->
# Update the `pkg` object with the new version.
pkg = grunt.file.readJSON('package.json')
grunt.config.data.pkg = concatOptions.process.data = pkg
grunt.log.ok('pkg reloaded.')
grunt.registerTask 'updcl', 'Update the changelog', (headerLevel) ->
headerPrefix = new Array(+headerLevel + 1).join '#'
{version} = grunt.config 'pkg'
today = grunt.template.today 'yyyy-mm-dd'
changelog = grunt.file.read 'CHANGELOG.md'
grunt.file.write 'CHANGELOG.md', "#{headerPrefix} #{version} - *#{today}*\n\n#{changelog}"
grunt.log.ok "Changelog updated for v#{version}."
grunt.registerTask 'updcl', 'Update the changelog', (i) ->
# i is the number of #s for markdown.
version = []
version.length = +i + 1
version = version.join('#') + ' v' + pkg.version + '\n*' + grunt.template.today('yyyy-mm-dd') + '*\n'
grunt.file.write 'CHANGELOG.md', version + '\n' + grunt.file.read('CHANGELOG.md')
grunt.log.ok 'Changelog updated for v' + pkg.version + '.'

0
LICENSE Normal file → Executable file
View File

2
README.md Normal file → Executable file
View File

@ -41,4 +41,4 @@ Note: this is only used to release new 4chan X versions, and is **not** needed o
- Edit the CoffeeScript sources.
- If the edits affect regular users, edit the changelog.
- Open a pull request.
- Open a pull request.

0
builds/4chan-X.js Normal file → Executable file
View File

0
builds/4chan-X.meta.js Normal file → Executable file
View File

2219
builds/4chan-X.user.js Normal file → Executable file

File diff suppressed because one or more lines are too long

0
builds/crx/icon128.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 196 B

After

Width:  |  Height:  |  Size: 196 B

0
builds/crx/icon16.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 157 B

After

Width:  |  Height:  |  Size: 157 B

0
builds/crx/icon48.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 204 B

After

Width:  |  Height:  |  Size: 204 B

3
builds/crx/manifest.json Normal file → Executable file
View File

@ -15,7 +15,8 @@
"run_at": "document_start"
}],
"homepage_url": "http://seaweedchan.github.io/4chan-x/",
"minimum_chrome_version": "26",
"minimum_chrome_version": "27",
"minimum_opera_version": "15",
"permissions": [
"storage"
]

2183
builds/crx/script.js Normal file → Executable file

File diff suppressed because one or more lines are too long

110
json/archives.json Executable file
View File

@ -0,0 +1,110 @@
[{
"uid": 0,
"name": "Foolz",
"domain": "archive.foolz.us",
"http": true,
"https": true,
"software": "foolfuuka",
"boards": ["a", "co", "gd", "jp", "m", "q", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"],
"files": ["a", "gd", "jp", "m", "q", "tg", "vg", "vp", "vr", "wsg"]
}, {
"uid": 1,
"name": "NSFW Foolz",
"domain": "nsfw.foolz.us",
"http": true,
"https": true,
"software": "foolfuuka",
"boards": ["u"],
"files": ["u"]
}, {
"uid": 2,
"name": "The Dark Cave",
"domain": "archive.thedarkcave.org",
"http": true,
"https": true,
"software": "foolfuuka",
"boards": ["c", "int", "out", "po"],
"files": ["c", "po"]
}, {
"uid": 3,
"name": "4plebs",
"domain": "archive.4plebs.org",
"http": true,
"https": false,
"software": "foolfuuka",
"boards": ["hr", "tg", "tv", "x"],
"files": ["hr", "tg", "tv", "x"]
}, {
"uid": 4,
"name": "Nyafuu",
"domain": "archive.nyafuu.org",
"http": true,
"https": true,
"software": "foolfuuka",
"boards": ["c", "w", "wg"],
"files": ["c", "w", "wg"]
}, {
"uid": 11,
"name": "Foolz a Shit",
"domain": "archive.foolzashit.com",
"http": true,
"https": true,
"software": "foolfuuka",
"boards": ["adv", "asp", "cm", "e", "i", "lgbt", "n", "o", "p", "pol", "s", "s4s", "t", "trv", "y"],
"files": ["adv", "asp", "cm", "e", "i", "lgbt", "n", "o", "p", "s", "s4s", "t", "trv", "y"]
}, {
"uid": 12,
"name": "fap archive",
"domain": "fuuka.worldathleticproject.org",
"http": true,
"https": false,
"software": "foolfuuka",
"boards": ["b", "e", "h", "hc", "p", "s", "u"],
"files": ["b", "e", "h", "hc", "p", "s", "u"]
}, {
"uid": 7,
"name": "Install Gentoo",
"domain": "archive.installgentoo.net",
"http": false,
"https": true,
"software": "fuuka",
"boards": ["diy", "g", "sci"],
"files": []
}, {
"uid": 8,
"name": "Rebecca Black Tech",
"domain": "rbt.asia",
"http": true,
"https": true,
"software": "fuuka",
"boards": ["cgl", "g", "mu", "w"],
"files": ["cgl", "g", "mu", "w"]
}, {
"uid": 9,
"name": "Heinessen",
"domain": "archive.heinessen.com",
"http": true,
"https": false,
"software": "fuuka",
"boards": ["an", "fit", "k", "mlp", "r9k", "toy"],
"files": ["an", "fit", "k", "r9k", "toy"]
}, {
"uid": 10,
"name": "warosu",
"domain": "fuuka.warosu.org",
"http": true,
"https": true,
"software": "fuuka",
"boards": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"],
"files": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"]
}, {
"uid": 13,
"name": "Foolz Beta",
"domain": "beta.foolz.us",
"http": true,
"https": true,
"withCredentials": true,
"software": "foolfuuka",
"boards": ["a", "co", "gd", "h", "jp", "m", "mlp", "q", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
"files": ["a", "gd", "h", "jp", "m", "q", "tg", "u", "vg", "vp", "vr", "wsg"]
}]

0
latest.js Normal file → Executable file
View File

11
package.json Normal file → Executable file
View File

@ -22,14 +22,15 @@
"devDependencies": {
"grunt": "~0.4.1",
"grunt-bump": "~0.0.11",
"grunt-concurrent": "~0.2.0",
"grunt-contrib-clean": "~0.4.1",
"grunt-concurrent": "~0.3.0",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-coffee": "~0.7.0",
"grunt-contrib-compress": "~0.5.1",
"grunt-contrib-compress": "~0.5.2",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-watch": "~0.4.4",
"grunt-shell": "~0.2.2"
"grunt-contrib-watch": "~0.5.0",
"grunt-shell": "~0.3.1",
"matchdep": "~0.1.2"
},
"repository": {
"type": "git",

201
src/Archive/Redirect.coffee Normal file → Executable file
View File

@ -1,117 +1,124 @@
Redirect =
thread: {}
post: {}
file: {}
data:
thread: {}
post: {}
file: {}
init: ->
for boardID, data of Conf['selectedArchives']
for type, id of data
for name, archive of Redirect.archives
continue if name isnt id or type is 'post' and archive.software isnt 'foolfuuka'
arr = if type is 'file'
archive.files
else
archive.boards
Redirect[type][boardID] = archive if arr.contains boardID
if archive = Redirect.archives[id]
boards = archive[type] or archive['boards']
continue unless boards.contains boardID
Redirect.data[type][boardID] = archive
for name, archive of Redirect.archives
for boardID in archive.boards
unless boardID of Redirect.thread
Redirect.thread[boardID] = archive
unless boardID of Redirect.post or archive.software isnt 'foolfuuka'
Redirect.post[boardID] = archive
unless boardID of Redirect.file or !archive.files.contains boardID
Redirect.file[boardID] = archive
unless boardID of Redirect.data.thread
Redirect.data.thread[boardID] = archive
unless boardID of Redirect.data.post or archive.software isnt 'foolfuuka'
Redirect.data.post[boardID] = archive
unless boardID of Redirect.data.file or !archive.files.contains boardID
Redirect.data.file[boardID] = archive
return
archives:
'Foolz':
'domain': 'archive.foolz.us'
'http': false
'https': true
'software': 'foolfuuka'
'boards': ['a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg']
'files': ['a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg']
'NSFW Foolz':
'domain': 'nsfw.foolz.us'
'http': false
'https': true
'software': 'foolfuuka'
'boards': ['u']
'files': ['u']
'The Dark Cave':
'domain': 'archive.thedarkcave.org'
'http': true
'https': true
'software': 'foolfuuka'
'boards': ['c', 'int', 'out', 'po']
'files': ['c', 'po']
'4plebs':
'domain': 'archive.4plebs.org'
'http': true
'software': 'foolfuuka'
'boards': ['hr', 'tg', 'tv', 'x']
'files': ['hr', 'tg', 'tv', 'x']
domain: 'archive.4plebs.org'
http: true
software: 'foolfuuka'
boards: ['hr', 'tg', 'tv', 'x']
files: ['hr', 'tg', 'tv', 'x']
'Nyafuu':
'domain': 'archive.nyafuu.org'
'http': true
'https': true
'software': 'foolfuuka'
'boards': ['c', 'w', 'wg']
'files': ['c', 'w', 'wg']
'fap archive':
domain: 'fuuka.worldathleticproject.org'
http: true
https: false
software: 'foolfuuka'
boards: ['b', 'e', 'h', 'hc', 'p', 's', 'u']
files: ['b', 'e', 'h', 'hc', 'p', 's', 'u']
'Foolz':
domain: 'archive.foolz.us'
http: false
https: true
software: 'foolfuuka'
boards: ['a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg']
files: ['a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg']
'Foolz a Shit':
'domain': 'archive.foolzashit.com'
'http': true
'https': true
'software': 'foolfuuka'
'boards': ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv']
'files': ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv']
domain: 'archive.foolzashit.com'
http: true
https: true
software: 'foolfuuka'
boards: ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv']
files: ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv']
'World Athletic Project':
'domain': 'fuuka.worldathleticproject.org'
'http': true
'https': false
'software': 'foolfuuka'
'boards': ['e', 'h', 'hc', 'p', 's', 'u']
'files': ['e', 'h', 'hc', 'p', 's', 'u']
'Install Gentoo':
'domain': 'archive.installgentoo.net'
'http': false
'https': true
'software': 'fuuka'
'boards': ['diy', 'g', 'sci']
'files': []
'warosu':
'domain': 'fuuka.warosu.org'
'http': true
'https': true
'software': 'fuuka'
'boards': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr']
'files': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr']
'Rebecca Black Tech':
'domain': 'rbt.asia'
'http': true
'https': true
'software': 'fuuka'
'boards': ['cgl', 'g', 'mu', 'w']
'files': ['cgl', 'g', 'mu', 'w']
'Foolz Beta':
domain: 'beta.foolz.us'
http: true
https: true
withCredentials: true
software: 'foolfuuka'
boards: ['a', 'co', 'gd', 'h', 'jp', 'm', 'mlp', 'q', 'sp', 'tg', 'tv', 'u', 'v', 'vg', 'vp', 'vr', 'wsg'],
files: ['a', 'gd', 'h', 'jp', 'm', 'q', 'tg', 'u', 'vg', 'vp', 'vr', 'wsg']
'Heinessen':
'domain': 'archive.heinessen.com'
'http': true
'software': 'fuuka'
'boards': ['an', 'fit', 'k', 'mlp', 'r9k', 'toy']
'files': ['an', 'k', 'toy']
domain: 'archive.heinessen.com'
http: true
software: 'fuuka'
boards: ['an', 'fit', 'k', 'mlp', 'r9k', 'toy']
files: ['an', 'k', 'toy']
'Install Gentoo':
domain: 'archive.installgentoo.net'
http: false
https: true
software: 'fuuka'
boards: ['diy', 'g', 'sci']
files: []
'NSFW Foolz':
domain: 'nsfw.foolz.us'
http: false
https: true
software: 'foolfuuka'
boards: ['u']
files: ['u']
'Nyafuu':
domain: 'archive.nyafuu.org'
http: true
https: true
software: 'foolfuuka'
boards: ['c', 'w', 'wg']
files: ['c', 'w', 'wg']
'Rebecca Black Tech':
domain: 'rbt.asia'
http: true
https: true
software: 'fuuka'
boards: ['cgl', 'g', 'mu', 'w']
files: ['cgl', 'g', 'mu', 'w']
'The Dark Cave':
domain: 'archive.thedarkcave.org'
http: true
https: true
software: 'foolfuuka'
boards: ['c', 'int', 'out', 'po']
files: ['c', 'po']
'warosu':
domain: 'fuuka.warosu.org'
http: true
https: true
software: 'fuuka'
boards: ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr']
files: ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr']
to: (dest, data) ->
archive = (if dest is 'search' then Redirect.thread else Redirect[dest])[data.boardID]
archive = (if dest is 'search' then Redirect.data.thread else Redirect.data[dest])[data.boardID]
return '' unless archive
Redirect[dest] archive, data
@ -144,7 +151,9 @@ Redirect =
# Remove necessary HTTPS procotol in September 2013.
if ['Foolz', 'NSFW Foolz'].contains archive.name
protocol = 'https://'
"#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}"
URL = new String "#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}"
URL.archive = archive
URL
file: (archive, {boardID, filename}) ->
"#{Redirect.protocol archive}#{archive.domain}/#{boardID}/full_image/#{filename}"

0
src/Filtering/Anonymize.coffee Normal file → Executable file
View File

2
src/Filtering/Filter.coffee Normal file → Executable file
View File

@ -209,7 +209,7 @@ Filter =
el = $.el 'a',
href: 'javascript:;'
textContent: text
el.setAttribute 'data-type', type
el.dataset.type = type
$.on el, 'click', Filter.menu.makeFilter
return {

25
src/Filtering/PostHiding.coffee Normal file → Executable file
View File

@ -17,7 +17,7 @@ PostHiding =
PostHiding.hide @, data.makeStub, data.hideRecursively
else
Recursive.apply PostHiding.hide, @, data.makeStub, true
Recursive.add PostHiding.hide, @, data.makeStub, true
Recursive.add PostHiding.hide, @, data.makeStub, true
return unless Conf['Reply Hiding Buttons']
$.replace $('.sideArrows', @nodes.root), PostHiding.makeButton @, 'hide'
@ -108,11 +108,12 @@ PostHiding =
PostHiding.hide post, makeStub, replies
else if replies
Recursive.apply PostHiding.hide, post, makeStub, true
Recursive.add PostHiding.hide, post, makeStub, true
Recursive.add PostHiding.hide, post, makeStub, true
else
return
PostHiding.saveHiddenState post, true, thisPost, makeStub, replies
$.event 'CloseMenu'
show: ->
parent = @parentNode
thisPost = $('input[name=thisPost]', parent).checked
@ -122,7 +123,7 @@ PostHiding =
PostHiding.show post, replies
else if replies
Recursive.apply PostHiding.show, post, true
Recursive.rm PostHiding.hide, post, true
Recursive.rm PostHiding.hide, post, true
else
return
if data = PostHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID}
@ -158,10 +159,7 @@ PostHiding =
toggle: ->
post = Get.postFromNode @
if post.isHidden
PostHiding.show post
else
PostHiding.hide post
PostHiding[(if post.isHidden then 'show' else 'hide')] post
PostHiding.saveHiddenState post, post.isHidden
hide: (post, makeStub=Conf['Stubs'], hideRecursively=Conf['Recursive Hiding']) ->
@ -170,7 +168,7 @@ PostHiding =
if hideRecursively
Recursive.apply PostHiding.hide, post, makeStub, true
Recursive.add PostHiding.hide, post, makeStub, true
Recursive.add PostHiding.hide, post, makeStub, true
for quotelink in Get.allQuotelinksLinkingTo post
$.addClass quotelink, 'filtered'
@ -184,13 +182,14 @@ PostHiding =
if Conf['Anonymize']
'Anonymous'
else
$('.nameBlock', post.nodes.info).textContent
post.info.name
$.add a, $.tn " #{postInfo}"
post.nodes.stub = $.el 'div',
className: 'stub'
$.add post.nodes.stub, a
if Conf['Menu']
$.add post.nodes.stub, [$.tn(' '), Menu.makeButton post]
$.add post.nodes.stub, unless Conf['Menu']
a
else
[a, $.tn(' '), button = Menu.makeButton post]
$.prepend post.nodes.root, post.nodes.stub
show: (post, showRecursively=Conf['Recursive Hiding']) ->
@ -202,7 +201,7 @@ PostHiding =
post.isHidden = false
if showRecursively
Recursive.apply PostHiding.show, post, true
Recursive.rm PostHiding.hide, post
Recursive.rm PostHiding.hide, post
for quotelink in Get.allQuotelinksLinkingTo post
$.rmClass quotelink, 'filtered'
return

0
src/Filtering/Recursive.coffee Normal file → Executable file
View File

59
src/Filtering/ThreadHiding.coffee Normal file → Executable file
View File

@ -71,11 +71,6 @@ ThreadHiding =
makeStub = $.el 'label',
innerHTML: "<input type=checkbox #{if Conf['Stubs'] then 'checked' else ''}> Make stub"
hideStubLink = $.el 'a',
textContent: 'Hide stub'
href: 'javascript:;'
$.on hideStubLink, 'click', ThreadHiding.menu.hideStub
$.event 'AddMenuEntry',
type: 'post'
el: div
@ -87,6 +82,27 @@ ThreadHiding =
true
subEntries: [el: apply; el: makeStub]
div = $.el 'a',
className: 'show-thread-link'
textContent: 'Show thread'
href: 'javascript:;'
$.on div, 'click', ThreadHiding.menu.show
$.event 'AddMenuEntry',
type: 'post'
el: div
order: 20
open: ({thread, isReply}) ->
if isReply or !thread.isHidden
return false
ThreadHiding.menu.thread = thread
true
hideStubLink = $.el 'a',
textContent: 'Hide stub'
href: 'javascript:;'
$.on hideStubLink, 'click', ThreadHiding.menu.hideStub
$.event 'AddMenuEntry',
type: 'post'
el: hideStubLink
@ -102,6 +118,13 @@ ThreadHiding =
ThreadHiding.hide thread, makeStub
ThreadHiding.saveHiddenState thread, makeStub
$.event 'CloseMenu'
show: ->
{thread} = ThreadHiding.menu
ThreadHiding.show thread
ThreadHiding.saveHiddenState thread
$.event 'CloseMenu'
hideStub: ->
{thread} = ThreadHiding.menu
ThreadHiding.hide thread, false
@ -113,7 +136,7 @@ ThreadHiding =
className: "#{type}-thread-button"
innerHTML: "<span class=fourchanx-link>&nbsp;#{if type is 'hide' then '-' else '+'}&nbsp;</span>"
href: 'javascript:;'
a.setAttribute 'data-fullid', thread.fullID
a.dataset.fullID = thread.fullID
$.on a, 'click', ThreadHiding.toggle
a
@ -134,7 +157,7 @@ ThreadHiding =
toggle: (thread) ->
unless thread instanceof Thread
thread = g.threads[@dataset.fullid]
thread = g.threads[@dataset.fullID]
if thread.isHidden
ThreadHiding.show thread
else
@ -150,24 +173,28 @@ ThreadHiding =
threadRoot.hidden = threadRoot.nextElementSibling.hidden = true # <hr>
return
numReplies = 0
if span = $ '.summary', threadRoot
numReplies = +span.textContent.match /\d+/
numReplies += $$('.opContainer ~ .replyContainer', threadRoot).length
numReplies = if numReplies is 1 then '1 reply' else "#{numReplies} replies"
numReplies = (
if span = $ '.summary', threadRoot
+span.textContent.match /\d+/
else
0
) +
$$('.opContainer ~ .replyContainer', threadRoot).length
numReplies = if numReplies is 1 then '1 reply' else "#{numReplies or 'No'} replies"
opInfo =
if Conf['Anonymize']
'Anonymous'
else
$('.nameBlock', OP.nodes.info).textContent
OP.info.name
a = ThreadHiding.makeButton thread, 'show'
$.add a, $.tn " #{opInfo} (#{numReplies})"
thread.stub = $.el 'div',
className: 'stub'
$.add thread.stub, a
if Conf['Menu']
$.add thread.stub, [$.tn(' '), Menu.makeButton OP]
$.add thread.stub, unless Conf['Menu']
a
else
[a, $.tn(' '), button = Menu.makeButton OP]
$.prepend threadRoot, thread.stub
show: (thread) ->

115
src/General/Build.coffee Normal file → Executable file
View File

@ -27,6 +27,7 @@ Build =
date: data.now
dateUTC: data.time
comment: data.com
capReps: data.capcode_replies
# thread status
isSticky: !!data.sticky
isClosed: !!data.closed
@ -58,7 +59,7 @@ Build =
postID, threadID, boardID
name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC
isSticky, isClosed
comment
comment, capReps
file
} = o
isOP = postID is threadID
@ -108,12 +109,12 @@ Build =
capcodeStart = ''
capcode = ''
flag =
if flagCode
" <img src='#{staticPath}country/#{if boardID is 'pol' then 'troll/' else ''}" +
flagCode.toLowerCase() + ".gif' alt=#{flagCode} title='#{flagName}' class=countryFlag>"
else
''
flag = unless flagCode
''
else if boardID is 'pol'
" <img src='#{staticPath}country/troll/#{flagCode.toLowerCase()}.gif' alt=#{flagCode} title='#{flagName}' class=countryFlag>"
else
" <span title='#{flagName}' class='flag flag-#{flagCode.toLowerCase()}'></span>"
if file?.isDeleted
fileHTML = if isOP
@ -176,78 +177,44 @@ Build =
else
fileHTML = ''
tripcode =
if tripcode
" <span class=postertrip>#{tripcode}</span>"
else
''
tripcode = if tripcode
" <span class=postertrip>#{tripcode}</span>"
else
''
sticky =
if isSticky
" <img src=#{staticPath}sticky.gif alt=Sticky title=Sticky class=stickyIcon>"
else
''
closed =
if isClosed
" <img src=#{staticPath}closed.gif alt=Closed title=Closed class=closedIcon>"
else
''
sticky = if isSticky
" <img src=#{staticPath}sticky.gif alt=Sticky title=Sticky class=stickyIcon>"
else
''
closed = if isClosed
" <img src=#{staticPath}closed.gif alt=Closed title=Closed class=closedIcon>"
else
''
capcodeReplies = ''
if capReps
generateCapcodeReplies = (capcodeType, array) ->
"<span class=smaller><span class=bold>#{
switch capcodeType
when 'admin'
'Administrator'
when 'mod'
'Moderator'
when 'developer'
'Developer'
} Repl#{if array.length > 1 then 'ies' else 'y'}:</span> #{
array.map (ID) ->
"<a href='/#{boardID}/res/#{threadID}#p#{ID}' class=quotelink>&gt;&gt;#{ID}</a>"
.join ' '
}</span><br>"
for capcodeType, array of capReps
capcodeReplies += generateCapcodeReplies capcodeType, array
capcodeReplies = "<br><br><span class=capcodeReplies>#{capcodeReplies}</span>"
container = $.el 'div',
id: "pc#{postID}"
className: "postContainer #{if isOP then 'op' else 'reply'}Container"
innerHTML: \
(if isOP then '' else "<div class=sideArrows id=sa#{postID}>&gt;&gt;</div>") +
"<div id=p#{postID} class='post #{if isOP then 'op' else 'reply'}#{
if capcode is 'admin_highlight'
' highlightPost'
else
''
}'>" +
"<div class='postInfoM mobile' id=pim#{postID}>" +
"<span class='nameBlock#{capcodeClass}'>" +
"<span class=name>#{name or ''}</span>" + tripcode +
capcodeStart + capcode + userID + flag + sticky + closed +
"<br>#{subject}" +
"</span><span class='dateTime postNum' data-utc=#{dateUTC}>#{date}" +
"<a href=#{"/#{boardID}/res/#{threadID}#p#{postID}"}>No.</a>" +
"<a href='#{
if g.VIEW is 'thread' and g.THREADID is +threadID
"javascript:quote(#{postID})"
else
"/#{boardID}/res/#{threadID}#q#{postID}"
}'>#{postID}</a>" +
'</span>' +
'</div>' +
(if isOP then fileHTML else '') +
"<div class='postInfo desktop' id=pi#{postID}>" +
"<input type=checkbox name=#{postID} value=delete> " +
"#{subject} " +
"<span class='nameBlock#{capcodeClass}'>" +
emailStart +
"<span class=name>#{name or ''}</span>" + tripcode +
capcodeStart + emailEnd + capcode + userID + flag + sticky + closed +
' </span> ' +
"<span class=dateTime data-utc=#{dateUTC}>#{date}</span> " +
"<span class='postNum desktop'>" +
"<a href=#{"/#{boardID}/res/#{threadID}#p#{postID}"} title='Highlight this post'>No.</a>" +
"<a href='#{
if g.VIEW is 'thread' and g.THREADID is +threadID
"javascript:quote(#{postID})"
else
"/#{boardID}/res/#{threadID}#q#{postID}"
}' title='Quote this post'>#{postID}</a>" +
'</span>' +
'</div>' +
(if isOP then '' else fileHTML) +
"<blockquote class=postMessage id=m#{postID}>#{comment or ''}</blockquote> " +
'</div>'
innerHTML: <%= grunt.file.read('src/General/html/Build/post.html').replace(/>\s+/g, '>').replace(/\s+</g, '<').replace(/\s+/g, ' ').trim() %>
for quote in $$ '.quotelink', container
href = quote.getAttribute 'href'

12
src/General/Config.coffee Normal file → Executable file
View File

@ -53,6 +53,10 @@ Config =
false
'Add buttons to navigate to top / bottom of thread.'
]
'Show Dice Roll': [
true
'Show dice that were entered into the email field.'
]
<% if (type !== 'crx') { %>
'Check for Updates': [
true
@ -641,6 +645,14 @@ q-replace
'x'
'Hide thread.'
]
'Previous Post Quoting You': [
'Alt+Up'
'Scroll to the previous post that quotes you.'
]
'Next Post Quoting You': [
'Alt+Down'
'Scroll to the next post that quotes you.'
]
updater:
checkbox:

78
src/General/Get.coffee Normal file → Executable file
View File

@ -18,8 +18,8 @@ Get =
post = g.posts["#{boardID}.#{postID}"]
if index then post.clones[index] else post
postFromNode: (root) ->
Get.postFromRoot $.x 'ancestor::div[contains(@class,"postContainer")][1]', root
contextFromLink: (quotelink) ->
Get.postFromRoot $.x '(ancestor::div[contains(@class,"postContainer")][1]|following::div[contains(@class,"postContainer")][1])', root
contextFromNode: (quotelink) ->
Get.postFromRoot $.x 'ancestor::div[parent::div[@class="thread"]][1]', quotelink
postDataFromLink: (link) ->
if link.hostname is 'boards.4chan.org'
@ -28,9 +28,8 @@ Get =
threadID = path[3]
postID = link.hash[2..]
else # resurrected quote
boardID = link.dataset.boardid
threadID = link.dataset.threadid or 0
postID = link.dataset.postid
{boardID, threadID, postID} = link.dataset
threadID or= 0
return {
boardID: boardID
threadID: +threadID
@ -72,8 +71,10 @@ Get =
$.cache "//api.4chan.org/#{boardID}/res/#{threadID}.json", ->
Get.fetchedPost @, boardID, threadID, postID, root, context
else if url = Redirect.to 'post', {boardID, postID}
$.cache url, ->
Get.archivedPost @, boardID, postID, root, context
$.cache url,
-> Get.archivedPost @, boardID, postID, root, context
,
withCredentials: url.archive.withCredentials
insert: (post, root, context) ->
# Stop here if the container has been removed while loading.
return unless root.parentNode
@ -98,8 +99,10 @@ Get =
unless [200, 304].contains status
# The thread can die by the time we check a quote.
if url = Redirect.to 'post', {boardID, postID}
$.cache url, ->
Get.archivedPost @, boardID, postID, root, context
$.cache url,
-> Get.archivedPost @, boardID, postID, root, context
,
withCredentials: url.archive.withCredentials
else
$.addClass root, 'warning'
root.textContent =
@ -116,8 +119,10 @@ Get =
if post.no > postID
# The post can be deleted by the time we check a quote.
if url = Redirect.to 'post', {boardID, postID}
$.cache url, ->
Get.archivedPost @, boardID, postID, root, context
$.cache url,
-> Get.archivedPost @, boardID, postID, root, context
,
withCredentials: url.archive.withCredentials
else
$.addClass root, 'warning'
root.textContent = "Post No.#{postID} was not found."
@ -154,30 +159,7 @@ Get =
| \[/?code\]
| \[/?moot\]
| \[/?banned\]
///g, (text) ->
switch text
when '\n'
'<br>'
when '[b]'
'<b>'
when '[/b]'
'</b>'
when '[spoiler]'
'<s>'
when '[/spoiler]'
'</s>'
when '[code]'
'<pre class=prettyprint>'
when '[/code]'
'</pre>'
when '[moot]'
'<div style="padding:5px;margin-left:.5em;border-color:#faa;border:2px dashed rgba(255,0,0,.1);border-radius:2px">'
when '[/moot]'
'</div>'
when '[banned]'
'<b style="color: red;">'
when '[/banned]'
'</b>'
///g, Get.parseMarkup
comment = bq.innerHTML
# greentext
@ -185,7 +167,7 @@ Get =
# quotes
.replace /((&gt;){2}(&gt;\/[a-z\d]+\/)?\d+)/g, '<span class=deadlink>$1</span>'
threadID = data.thread_num
threadID = +data.thread_num
o =
# id
postID: "#{postID}"
@ -229,3 +211,27 @@ Get =
isArchived: true
Main.callbackNodes Post, [post]
Get.insert post, root, context
parseMarkup: (text) ->
switch text
when '\n'
'<br>'
when '[b]'
'<b>'
when '[/b]'
'</b>'
when '[spoiler]'
'<s>'
when '[/spoiler]'
'</s>'
when '[code]'
'<pre class=prettyprint>'
when '[/code]'
'</pre>'
when '[moot]'
'<div style="padding:5px;margin-left:.5em;border-color:#faa;border:2px dashed rgba(255,0,0,.1);border-radius:2px">'
when '[/moot]'
'</div>'
when '[banned]'
'<b style="color: red;">'
when '[/banned]'
'</b>'

6
src/General/Globals.coffee Normal file → Executable file
View File

@ -1,9 +1,3 @@
<% if (type === 'userjs') { %>
# Opera doesn't support the @match metadata key,
# return 4chan X here if we're not on 4chan.
return unless /^(boards|images|sys)\.4chan\.org$/.test location.hostname
<% } %>
Conf = {}
c = console
d = document

15
src/General/Header.coffee Normal file → Executable file
View File

@ -70,7 +70,7 @@ Header =
return unless Main.isThisPageLegit()
# Wait for #boardNavMobile instead of #boardNavDesktop,
# it might be incomplete otherwise.
$.asap (-> $.id('boardNavMobile') or d.readyState in ['interactive', 'complete']), Header.setBoardList
$.asap (-> $.id('boardNavMobile') or d.readyState isnt 'loading'), Header.setBoardList
$.prepend d.body, @bar
$.add d.body, Header.hover
@setBarPosition Conf['Bottom Header']
@ -131,7 +131,7 @@ Header =
list = $ '#custom-board-list', Header.bar
$.rmAll list
return unless text
as = $$('#full-board-list a', Header.bar)
as = $$ '#full-board-list a[title]', Header.bar
nodes = text.match(/[\w@]+((-(all|title|replace|full|index|catalog|url:"[^"]+[^"]"|text:"[^"]+")|\,"[^"]+[^"]"))*|[^\w@]+/g).map (t) ->
if /^[^\w@]/.test t
return $.tn t
@ -166,10 +166,13 @@ Header =
a.textContent
if m = t.match /-(index|catalog)/
a.setAttribute 'data-only', m[1]
a.dataset.only = m[1]
a.href = "//boards.4chan.org/#{board}/"
if m[1] is 'catalog'
a.href += 'catalog'
if Conf['External Catalog']
a.href = CatalogLinks.external board
else
a.href += 'catalog'
$.addClass a, 'catalog'
$.addClass a, 'navSmall' if board is '@'
@ -302,8 +305,8 @@ Header =
{top} = post.getBoundingClientRect()
if Conf['Fixed Header'] and not Conf['Bottom Header']
headRect = Header.bar.getBoundingClientRect()
top += - headRect.top - headRect.height
<% if (type === 'crx') { %>d.body<% } else { %>doc<% } %>.scrollTop += top
top -= headRect.top + headRect.height
window.scrollBy 0, top
addShortcut: (el) ->
shortcut = $.el 'span',

143
src/General/Main.coffee Normal file → Executable file
View File

@ -15,11 +15,12 @@ Main =
for db in DataBoards
Conf[db] = boards: {}
Conf['selectedArchives'] = {}
Conf['CachedTitles'] = []
$.get Conf, Main.initFeatures
$.on d, '4chanMainInit', Main.initStyle
$.asap (-> d.head and $('link[rel="shortcut icon"]', d.head) or d.readyState in ['interactive', 'complete']),
$.asap (-> d.head and $('link[rel="shortcut icon"]', d.head) or d.readyState isnt 'loading'),
Main.initStyle
initFeatures: (items) ->
@ -47,7 +48,7 @@ Main =
return
when 'images.4chan.org'
$.ready ->
if Conf['404 Redirect'] and d.title is '4chan - 404 Not Found'
if Conf['404 Redirect'] and ['4chan - Temporarily Offline', '4chan - 404 Not Found'].contains d.title
Redirect.init()
pathname = location.pathname.split '/'
URL = Redirect.to 'file',
@ -72,61 +73,62 @@ Main =
# c.time 'All initializations'
init
'Polyfill': Polyfill
'Redirect': Redirect
'Header': Header
'Catalog Links': CatalogLinks
'Settings': Settings
'Announcement Hiding': PSAHiding
'Fourchan thingies': Fourchan
'Emoji': Emoji
'Color User IDs': IDColor
'Remove Spoilers': RemoveSpoilers
'Custom CSS': CustomCSS
'Linkify': Linkify
'Resurrect Quotes': Quotify
'Filter': Filter
'Thread Hiding Buttons': ThreadHiding
'Reply Hiding Buttons': PostHiding
'Recursive': Recursive
'Strike-through Quotes': QuoteStrikeThrough
'Quick Reply': QR
'Menu': Menu
'Report Link': ReportLink
'Thread Hiding (Menu)': ThreadHiding.menu
'Reply Hiding (Menu)': PostHiding.menu
'Delete Link': DeleteLink
'Filter (Menu)': Filter.menu
'Download Link': DownloadLink
'Archive Link': ArchiveLink
'Quote Inlining': QuoteInline
'Quote Previewing': QuotePreview
'Quote Backlinks': QuoteBacklink
'Mark Quotes of You': QuoteYou
'Mark OP Quotes': QuoteOP
'Mark Cross-thread Quotes': QuoteCT
'Anonymize': Anonymize
'Time Formatting': Time
'Relative Post Dates': RelativeDates
'File Info Formatting': FileInfo
'Fappe Tyme': FappeTyme
'Sauce': Sauce
'Image Expansion': ImageExpand
'Image Expansion (Menu)': ImageExpand.menu
'Reveal Spoilers': RevealSpoilers
'Image Loading': ImageLoader
'Image Hover': ImageHover
'Comment Expansion': ExpandComment
'Thread Expansion': ExpandThread
'Thread Excerpt': ThreadExcerpt
'Favicon': Favicon
'Unread': Unread
'Quote Threading': QuoteThreading
'Thread Stats': ThreadStats
'Thread Updater': ThreadUpdater
'Thread Watcher': ThreadWatcher
'Index Navigation': Nav
'Keybinds': Keybinds
'Polyfill': Polyfill
'Redirect': Redirect
'Header': Header
'Catalog Links': CatalogLinks
'Settings': Settings
'Announcement Hiding': PSAHiding
'Fourchan thingies': Fourchan
'Emoji': Emoji
'Color User IDs': IDColor
'Reveal Spoilers': RemoveSpoilers
'Custom CSS': CustomCSS
'Linkify': Linkify
'Resurrect Quotes': Quotify
'Filter': Filter
'Thread Hiding Buttons': ThreadHiding
'Reply Hiding Buttons': PostHiding
'Recursive': Recursive
'Strike-through Quotes': QuoteStrikeThrough
'Quick Reply': QR
'Menu': Menu
'Report Link': ReportLink
'Thread Hiding (Menu)': ThreadHiding.menu
'Reply Hiding (Menu)': PostHiding.menu
'Delete Link': DeleteLink
'Filter (Menu)': Filter.menu
'Download Link': DownloadLink
'Archive Link': ArchiveLink
'Quote Inlining': QuoteInline
'Quote Previewing': QuotePreview
'Quote Backlinks': QuoteBacklink
'Mark Quotes of You': QuoteYou
'Mark OP Quotes': QuoteOP
'Mark Cross-thread Quotes': QuoteCT
'Anonymize': Anonymize
'Time Formatting': Time
'Relative Post Dates': RelativeDates
'File Info Formatting': FileInfo
'Fappe Tyme': FappeTyme
'Sauce': Sauce
'Image Expansion': ImageExpand
'Image Expansion (Menu)': ImageExpand.menu
'Reveal Spoiler Thumbnails': RevealSpoilers
'Image Loading': ImageLoader
'Image Hover': ImageHover
'Comment Expansion': ExpandComment
'Thread Expansion': ExpandThread
'Thread Excerpt': ThreadExcerpt
'Favicon': Favicon
'Unread': Unread
'Quote Threading': QuoteThreading
'Thread Stats': ThreadStats
'Thread Updater': ThreadUpdater
'Thread Watcher': ThreadWatcher
'Index Navigation': Nav
'Keybinds': Keybinds
'Show Dice Roll': Dice
# c.timeEnd 'All initializations'
@ -139,10 +141,7 @@ Main =
# disable the mobile layout
$('link[href*=mobile]', d.head)?.disabled = true
<% if (type === 'crx') { %>
$.addClass doc, 'webkit'
$.addClass doc, 'blink'
<% } else if (type === 'userjs') { %>
$.addClass doc, 'presto'
<% } else { %>
$.addClass doc, 'gecko'
<% } %>
@ -166,13 +165,9 @@ Main =
$.addClass doc, style
setStyle()
return unless mainStyleSheet
if window.MutationObserver
observer = new MutationObserver setStyle
observer.observe mainStyleSheet,
attributes: true
attributeFilter: ['href']
else
$.on mainStyleSheet, 'DOMAttrModified', setStyle
new MutationObserver(setStyle).observe mainStyleSheet,
attributes: true
attributeFilter: ['href']
initReady: ->
if d.title is '4chan - 404 Not Found'
@ -192,20 +187,18 @@ Main =
threads = []
posts = []
for boardChild in board.children
continue unless $.hasClass boardChild, 'thread'
thread = new Thread boardChild.id[1..], g.BOARD
for threadRoot in $$ '.board > .thread', board
thread = new Thread +threadRoot.id[1..], g.BOARD
threads.push thread
for threadChild in boardChild.children
continue unless $.hasClass threadChild, 'postContainer'
for postRoot in $$ '.thread > .postContainer', threadRoot
try
posts.push new Post threadChild, thread, g.BOARD
posts.push new Post postRoot, thread, g.BOARD
catch err
# Skip posts that we failed to parse.
unless errors
errors = []
errors.push
message: "Parsing of Post No.#{threadChild.id.match(/\d+/)} failed. Post will be skipped."
message: "Parsing of Post No.#{postRoot.id.match(/\d+/)} failed. Post will be skipped."
error: err
Main.handleErrors errors if errors
@ -373,7 +366,7 @@ Main =
unless 'thisPageIsLegit' of Main
Main.thisPageIsLegit = location.hostname is 'boards.4chan.org' and
!$('link[href*="favicon-status.ico"]', d.head) and
d.title not in ['4chan - Temporarily Offline', '4chan - Error']
d.title not in ['4chan - Temporarily Offline', '4chan - Error', '504 Gateway Time-out']
Main.thisPageIsLegit
css: """

4
src/General/Settings.coffee Normal file → Executable file
View File

@ -262,8 +262,7 @@ Settings =
'%board'
else
c
for key, val of Config.hotkeys
continue unless key of data.Conf
for key, val of Config.hotkeys when key of data.Conf
data.Conf[key] = data.Conf[key].replace(/ctrl|alt|meta/g, (s) -> "#{s[0].toUpperCase()}#{s[1..]}").replace /(^|.+\+)[A-Z]$/g, (s) ->
"Shift+#{s[0...-1]}#{s[-1..].toLowerCase()}"
data.Conf.WatchedThreads = data.WatchedThreads
@ -467,7 +466,6 @@ Settings =
$.cb.checked.call @
usercss: ->
CustomCSS.update()
keybinds: (section) ->
section.innerHTML = """
<%= grunt.file.read('src/General/html/Settings/Keybinds.html').replace(/>\s+</g, '><').trim() %>

25
src/General/UI.coffee Normal file → Executable file
View File

@ -70,8 +70,8 @@ UI = do ->
# Position
mRect = menu.getBoundingClientRect()
bRect = button.getBoundingClientRect()
bTop = doc.scrollTop + d.body.scrollTop + bRect.top
bLeft = doc.scrollLeft + d.body.scrollLeft + bRect.left
bTop = window.scrollY + bRect.top
bLeft = window.scrollX + bRect.left
cHeight = doc.clientHeight
cWidth = doc.clientWidth
[top, bottom] = if bRect.top + bRect.height + mRect.height < cHeight
@ -306,12 +306,12 @@ UI = do ->
hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb}) ->
o = {
root: root
el: el
style: el.style
cb: cb
endEvents: endEvents
latestEvent: latestEvent
root
el
style: el.style
cb
endEvents
latestEvent
clientHeight: doc.clientHeight
clientWidth: doc.clientWidth
}
@ -327,6 +327,11 @@ UI = do ->
if $.x 'ancestor::div[contains(@class,"inline")][1]', root
$.on d, 'keydown', o.hoverend
$.on root, 'mousemove', o.hover
<% if (type === 'userscript') { %>
# Workaround for https://github.com/MayhemYDG/4chan-x/issues/377
o.workaround = (e) -> o.hoverend() unless root.contains e.target
$.on doc, 'mousemove', o.workaround
<% } %>
hover = (e) ->
@latestEvent = e
@ -357,6 +362,10 @@ UI = do ->
$.off @root, @endEvents, @hoverend
$.off d, 'keydown', @hoverend
$.off @root, 'mousemove', @hover
<% if (type === 'userscript') { %>
# Workaround for https://github.com/MayhemYDG/4chan-x/issues/377
$.off doc, 'mousemove', @workaround
<% } %>
@cb.call @ if @cb

0
src/General/audio/beep.wav Normal file → Executable file
View File

0
src/General/css/burichan.css Normal file → Executable file
View File

0
src/General/css/futaba.css Normal file → Executable file
View File

0
src/General/css/photon.css Normal file → Executable file
View File

34
src/General/css/style.css Normal file → Executable file
View File

@ -304,10 +304,8 @@ a {
box-sizing: border-box;
box-shadow: 0 0 15px rgba(0, 0, 0, .15);
height: 600px;
min-height: 0;
max-height: 100%;
width: 900px;
min-width: 0;
max-width: 100%;
margin: auto;
padding: 3px;
@ -315,7 +313,6 @@ a {
left: 50%;
-moz-transform: translate(-50%, -50%);
-webkit-transform: translate(-50%, -50%);
-o-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
#fourchanx-settings > nav {
@ -392,14 +389,17 @@ a {
position: absolute;
}
.section-advanced .note {
font-size: 0.8em;
font-style: italic;
font-size: 0.8em;
font-style: italic;
margin-left: 10px;
}
.section-advanced .note code {
font-style: normal;
font-size: 11px;
}
.section-keybinds .field {
font-family: monospace;
}
#fourchanx-settings fieldset {
border: 1px solid;
border-radius: 3px;
@ -533,7 +533,8 @@ a.hide-announcement {
.deadlink {
text-decoration: none !important;
}
.backlink.deadlink:not(.forwardlink), .quotelink.deadlink:not(.forwardlink) {
.backlink.deadlink:not(.forwardlink),
.quotelink.deadlink:not(.forwardlink) {
text-decoration: underline !important;
}
.inlined {
@ -576,16 +577,14 @@ a.hide-announcement {
padding: 2px 2px 5px;
}
#qp img {
max-height: 300px;
max-width: 500px;
max-height: 80vh;
max-width: 50vw;
}
.qphl {
outline: 2px solid rgba(216, 94, 49, .7);
}
:root.highlight-own .yourPost>.reply,
:root.highlight-you .quotesYou>.reply {
:root.highlight-own .yourPost > .reply,
:root.highlight-you .quotesYou > .reply {
border-left: 2px solid rgba(221,0,0,.5);
}
/* Quote Threading */
@ -613,8 +612,7 @@ a.hide-announcement {
:root.fit-width .full-image {
max-width: 100%;
}
:root.gecko.fit-width .full-image,
:root.presto.fit-width .full-image {
:root.gecko.fit-width .full-image {
width: 100%;
}
#ihover {
@ -671,7 +669,10 @@ a.hide-announcement {
#file-n-submit:not(.has-file) #qr-filerm {
display: none;
}
#qr select, #dump-button, .remove, .captcha-img {
#qr select,
#dump-button,
.remove,
.captcha-img {
cursor: pointer;
}
#qr {
@ -728,7 +729,8 @@ a.hide-announcement {
height: 9em;
}
input.field.tripped:not(:hover):not(:focus) {
color: transparent !important; text-shadow: none !important;
color: transparent !important;
text-shadow: none !important;
}
#qr textarea {
resize: both;
@ -910,7 +912,9 @@ a:only-of-type > .remove {
.qr-preview > label {
background: rgba(0,0,0,.5);
color: #fff;
right: 0; bottom: 0; left: 0;
right: 0;
bottom: 0;
left: 0;
position: absolute;
text-align: center;
}

0
src/General/css/tomorrow.css Normal file → Executable file
View File

0
src/General/css/yotsuba-b.css Normal file → Executable file
View File

0
src/General/css/yotsuba.css Normal file → Executable file
View File

View File

@ -0,0 +1,59 @@
"""#{if isOP then '' else "<div class=sideArrows id=sa#{postID}>&gt;&gt;</div>"}
<div id=p#{postID} class='post #{if isOP then 'op' else 'reply'}#{
if capcode is 'admin_highlight' then
' highlightPost'
else
''
}'>
<div class='postInfoM mobile' id=pim#{postID}>
<span class='nameBlock#{capcodeClass}'>
<span class=name>
#{name or ''}
</span>
#{tripcode + capcodeStart + capcode + userID + flag + sticky + closed}
<br>#{subject}
</span>
<span class='dateTime postNum' data-utc=#{dateUTC}>
#{date}
<a href=#{"/#{boardID}/res/#{threadID}#p#{postID}"}>
No.
</a>
<a href='#{
if g.VIEW is 'thread' and g.THREADID is +threadID then
"javascript:quote(#{postID})"
else
"/#{boardID}/res/#{threadID}#q#{postID}"
}'>
#{postID}
</a>
</span>
</div>
#{if isOP then fileHTML else ''}
<div class='postInfo desktop' id=pi#{postID}>
<input type=checkbox name=#{postID} value=delete>
#{subject}
<span class='nameBlock#{capcodeClass}'>
#{emailStart}
<span class=name>#{name or ''}</span>
#{tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed}
</span>#{" "}
<span class=dateTime data-utc=#{dateUTC}>#{date}</span>#{" "}
<span class='postNum desktop'>
<a href=#{"/#{boardID}/res/#{threadID}#p#{postID}"} title='Highlight this post'>No.</a>
<a href='#{
if g.VIEW is 'thread' and g.THREADID is +threadID then
"javascript:quote(#{postID})"
else
"/#{boardID}/res/#{threadID}#q#{postID}"
}' title='Quote this post'>#{postID}</a>
</span>
</div>
#{if isOP then '' else fileHTML}
<blockquote class=postMessage id=m#{postID}>#{comment or ''}#{capcodeReplies}</blockquote>#{" "}
</div>"""

0
src/General/html/Features/QuickReply.html Normal file → Executable file
View File

0
src/General/html/Settings/Advanced.html Normal file → Executable file
View File

4
src/General/html/Settings/Filter-guide.html Normal file → Executable file
View File

@ -1,6 +1,6 @@
<div class=warning #{if Conf['Filter'] then 'hidden' else ''}><code>Filter</code> is disabled.</div>
<p>
Use <a href=https://developer.mozilla.org/en/JavaScript/Guide/Regular_Expressions>regular expressions</a>, one per line.<br>
Use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions">regular expressions</a>, one per line.<br>
Lines starting with a <code>#</code> will be ignored.<br>
For example, <code>/weeaboo/i</code> will filter posts containing the string `<code>weeaboo</code>`, case-insensitive.<br>
MD5 filtering uses exact string matching, not regular expressions.
@ -26,4 +26,4 @@
Highlighted OPs will have their threads put on top of board pages by default.<br>
For example: <code>top:yes;</code> or <code>top:no;</code>.
</li>
</ul>
</ul>

0
src/General/html/Settings/Filter-select.html Normal file → Executable file
View File

0
src/General/html/Settings/Keybinds.html Normal file → Executable file
View File

0
src/General/html/Settings/Sauce.html Normal file → Executable file
View File

0
src/General/html/Settings/Settings.html Normal file → Executable file
View File

0
src/General/img/changelog/1.1.18.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

0
src/General/img/changelog/1.2.0.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

0
src/General/img/emoji/SS-sage.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 576 B

After

Width:  |  Height:  |  Size: 576 B

0
src/General/img/emoji/appchan-sage.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 659 B

After

Width:  |  Height:  |  Size: 659 B

0
src/General/img/emoji/arch.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 567 B

After

Width:  |  Height:  |  Size: 567 B

0
src/General/img/emoji/baka.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 987 B

After

Width:  |  Height:  |  Size: 987 B

0
src/General/img/emoji/centos.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 858 B

After

Width:  |  Height:  |  Size: 858 B

0
src/General/img/emoji/crunchbang.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 297 B

After

Width:  |  Height:  |  Size: 297 B

0
src/General/img/emoji/debian.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 559 B

After

Width:  |  Height:  |  Size: 559 B

0
src/General/img/emoji/fedora.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 631 B

After

Width:  |  Height:  |  Size: 631 B

0
src/General/img/emoji/freebsd.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

0
src/General/img/emoji/gentoo.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 882 B

0
src/General/img/emoji/gnu.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

0
src/General/img/emoji/madotsuki.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 231 B

After

Width:  |  Height:  |  Size: 231 B

0
src/General/img/emoji/mint.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1006 B

After

Width:  |  Height:  |  Size: 1006 B

0
src/General/img/emoji/neko.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1012 B

After

Width:  |  Height:  |  Size: 1012 B

0
src/General/img/emoji/openbsd.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1002 B

After

Width:  |  Height:  |  Size: 1002 B

0
src/General/img/emoji/osx.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 820 B

After

Width:  |  Height:  |  Size: 820 B

0
src/General/img/emoji/plan9.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 668 B

After

Width:  |  Height:  |  Size: 668 B

0
src/General/img/emoji/ponyo.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 884 B

After

Width:  |  Height:  |  Size: 884 B

0
src/General/img/emoji/rabite.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

0
src/General/img/emoji/rhel.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 797 B

After

Width:  |  Height:  |  Size: 797 B

0
src/General/img/emoji/sabayon.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 830 B

After

Width:  |  Height:  |  Size: 830 B

0
src/General/img/emoji/sakamoto.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 934 B

After

Width:  |  Height:  |  Size: 934 B

0
src/General/img/emoji/sega.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 339 B

0
src/General/img/emoji/slackware.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 912 B

After

Width:  |  Height:  |  Size: 912 B

0
src/General/img/emoji/trisquel.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 820 B

After

Width:  |  Height:  |  Size: 820 B

0
src/General/img/emoji/ubuntu.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 625 B

After

Width:  |  Height:  |  Size: 625 B

0
src/General/img/emoji/windows.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

0
src/General/img/emoji/yuno.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

0
src/General/img/favicons/Mayhem/unreadDead.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 346 B

After

Width:  |  Height:  |  Size: 346 B

0
src/General/img/favicons/Mayhem/unreadDeadY.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 456 B

After

Width:  |  Height:  |  Size: 456 B

0
src/General/img/favicons/Mayhem/unreadNSFW.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 323 B

After

Width:  |  Height:  |  Size: 323 B

0
src/General/img/favicons/Mayhem/unreadNSFWY.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 450 B

0
src/General/img/favicons/Mayhem/unreadSFW.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 321 B

0
src/General/img/favicons/Mayhem/unreadSFWY.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 450 B

0
src/General/img/favicons/Original/unreadDead.gif Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 110 B

0
src/General/img/favicons/Original/unreadDeadY.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 232 B

After

Width:  |  Height:  |  Size: 232 B

0
src/General/img/favicons/Original/unreadNSFW.gif Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 110 B

0
src/General/img/favicons/Original/unreadNSFWY.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 232 B

After

Width:  |  Height:  |  Size: 232 B

0
src/General/img/favicons/Original/unreadSFW.gif Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 110 B

0
src/General/img/favicons/Original/unreadSFWY.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 232 B

After

Width:  |  Height:  |  Size: 232 B

0
src/General/img/favicons/dead.gif Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 94 B

After

Width:  |  Height:  |  Size: 94 B

0
src/General/img/favicons/empty.gif Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 94 B

After

Width:  |  Height:  |  Size: 94 B

0
src/General/img/favicons/exclamation.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 114 B

After

Width:  |  Height:  |  Size: 114 B

0
src/General/img/favicons/ferongr/unreadDead.gif Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 172 B

After

Width:  |  Height:  |  Size: 172 B

0
src/General/img/favicons/ferongr/unreadDeadY.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 274 B

After

Width:  |  Height:  |  Size: 274 B

0
src/General/img/favicons/ferongr/unreadNSFW.gif Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 172 B

After

Width:  |  Height:  |  Size: 172 B

0
src/General/img/favicons/ferongr/unreadNSFWY.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 274 B

After

Width:  |  Height:  |  Size: 274 B

0
src/General/img/favicons/ferongr/unreadSFW.gif Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 172 B

After

Width:  |  Height:  |  Size: 172 B

0
src/General/img/favicons/ferongr/unreadSFWY.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 270 B

After

Width:  |  Height:  |  Size: 270 B

0
src/General/img/favicons/xat-/unreadDead.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 273 B

After

Width:  |  Height:  |  Size: 273 B

0
src/General/img/favicons/xat-/unreadDeadY.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 349 B

After

Width:  |  Height:  |  Size: 349 B

0
src/General/img/favicons/xat-/unreadNSFW.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 281 B

After

Width:  |  Height:  |  Size: 281 B

Some files were not shown because too many files have changed in this diff Show More