Merge branch 'v3' into Av2
Conflicts: builds/4chan-X.js builds/4chan-X.user.js builds/crx.crx builds/crx/script.js src/config.coffee src/css/style.css src/features/misc/announcementhiding.coffee src/features/misc/header.coffee src/features/misc/keybinds.coffee src/main.coffee src/settings.coffee
This commit is contained in:
commit
3d07017708
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,7 +1,6 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
tmp/
|
|
||||||
4chan_x.user.js
|
|
||||||
Cakefile
|
|
||||||
script.coffee
|
|
||||||
*~
|
*~
|
||||||
*.db
|
*.db
|
||||||
|
tmp-crx/
|
||||||
|
tmp-userjs/
|
||||||
|
tmp-userscript/
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
- Added the option `Hide Unread Count at (0)`, disabled by default.
|
||||||
|
|
||||||
### 3.1.4 - *2013-04-17*
|
### 3.1.4 - *2013-04-17*
|
||||||
|
|
||||||
- Fix QR remembering the file spoiler state when it shouldn't, for real this time.
|
- Fix QR remembering the file spoiler state when it shouldn't, for real this time.
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
## Reporting bugs
|
## Reporting bugs and suggestions
|
||||||
|
|
||||||
|
Reporting bugs:
|
||||||
|
|
||||||
1. Make sure both your **browser** and **4chan X** are up to date.
|
1. Make sure both your **browser** and **4chan X** are up to date.
|
||||||
2. Disable your other extensions & scripts to identify conflicts.
|
2. Disable your other extensions & scripts to identify conflicts.
|
||||||
@ -13,6 +15,12 @@ Open your console with:
|
|||||||
- `Ctrl + Shift + K` on Firefox.
|
- `Ctrl + Shift + K` on Firefox.
|
||||||
- `Ctrl + Shift + O` on Opera.
|
- `Ctrl + Shift + O` on Opera.
|
||||||
|
|
||||||
|
Respect these guidelines:
|
||||||
|
- Describe the issue clearly, put some effort into it. A one-liner isn't a good enough description.
|
||||||
|
- If you want to get your suggestion implemented sooner, make it convincing.
|
||||||
|
- If you want to criticize, make it convincing and constructive.
|
||||||
|
- Be mature. Act like an idiot and you will be blocked without warning.
|
||||||
|
|
||||||
## Development & Contribution
|
## Development & Contribution
|
||||||
|
|
||||||
### Get started
|
### Get started
|
||||||
|
|||||||
@ -5,6 +5,11 @@ module.exports = (grunt) ->
|
|||||||
process:
|
process:
|
||||||
data: pkg
|
data: pkg
|
||||||
|
|
||||||
|
shellOptions =
|
||||||
|
stdout: true
|
||||||
|
stderr: true
|
||||||
|
failOnError: true
|
||||||
|
|
||||||
# Project configuration.
|
# Project configuration.
|
||||||
grunt.initConfig
|
grunt.initConfig
|
||||||
pkg: pkg
|
pkg: pkg
|
||||||
@ -12,12 +17,12 @@ module.exports = (grunt) ->
|
|||||||
coffee:
|
coffee:
|
||||||
options: concatOptions
|
options: concatOptions
|
||||||
src: [
|
src: [
|
||||||
'src/code/config.coffee'
|
'src/config.coffee'
|
||||||
'src/code/globals.coffee'
|
'src/globals.coffee'
|
||||||
'src/lib/*.coffee'
|
'src/lib/*.coffee'
|
||||||
'src/code/*/*.coffee'
|
'src/features/*/*.coffee'
|
||||||
'src/code/settings.coffee'
|
'src/settings.coffee'
|
||||||
'src/code/main.coffee'
|
'src/main.coffee'
|
||||||
]
|
]
|
||||||
dest: 'tmp-<%= pkg.type %>/script.coffee'
|
dest: 'tmp-<%= pkg.type %>/script.coffee'
|
||||||
|
|
||||||
@ -68,27 +73,25 @@ module.exports = (grunt) ->
|
|||||||
'build-userscript'
|
'build-userscript'
|
||||||
]
|
]
|
||||||
|
|
||||||
exec:
|
shell:
|
||||||
commit:
|
commit:
|
||||||
command: ->
|
options: shellOptions
|
||||||
release = "#{pkg.meta.name} v#{pkg.version}"
|
command: [
|
||||||
return [
|
'git checkout <%= pkg.meta.mainBranch %>',
|
||||||
'git checkout ' + pkg.meta.mainBranch,
|
'git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>."',
|
||||||
'git commit -am "Release ' + release + '."',
|
'git tag -a <%= pkg.version %> -m "<%= pkg.meta.name %> v<%= pkg.version %>."',
|
||||||
'git tag -a ' + pkg.version + ' -m "' + release + '."',
|
'git tag -af stable-v3 -m "<%= pkg.meta.name %> v<%= pkg.version %>."'
|
||||||
'git tag -af stable-v3 -m "' + release + '."'
|
].join(' && ')
|
||||||
].join(' && ');
|
|
||||||
stdout: true
|
stdout: true
|
||||||
|
|
||||||
push:
|
push:
|
||||||
command: 'git push origin --all && git push origin --tags'
|
options: shellOptions
|
||||||
stdout: true
|
command: 'git push origin --tags -f && git push origin --all'
|
||||||
|
|
||||||
watch:
|
watch:
|
||||||
all:
|
all:
|
||||||
options:
|
options:
|
||||||
interrupt: true
|
interrupt: true
|
||||||
nospawn: true
|
|
||||||
files: [
|
files: [
|
||||||
'Gruntfile.coffee'
|
'Gruntfile.coffee'
|
||||||
'package.json'
|
'package.json'
|
||||||
@ -120,7 +123,7 @@ module.exports = (grunt) ->
|
|||||||
grunt.loadNpmTasks 'grunt-contrib-concat'
|
grunt.loadNpmTasks 'grunt-contrib-concat'
|
||||||
grunt.loadNpmTasks 'grunt-contrib-copy'
|
grunt.loadNpmTasks 'grunt-contrib-copy'
|
||||||
grunt.loadNpmTasks 'grunt-contrib-watch'
|
grunt.loadNpmTasks 'grunt-contrib-watch'
|
||||||
grunt.loadNpmTasks 'grunt-exec'
|
grunt.loadNpmTasks 'grunt-shell'
|
||||||
|
|
||||||
grunt.registerTask 'default', [
|
grunt.registerTask 'default', [
|
||||||
'build'
|
'build'
|
||||||
@ -161,8 +164,8 @@ module.exports = (grunt) ->
|
|||||||
|
|
||||||
grunt.registerTask 'release', [
|
grunt.registerTask 'release', [
|
||||||
'default'
|
'default'
|
||||||
'exec:commit'
|
'shell:commit'
|
||||||
'exec:push'
|
'shell:push'
|
||||||
]
|
]
|
||||||
|
|
||||||
grunt.registerTask 'patch', [
|
grunt.registerTask 'patch', [
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
10
package.json
10
package.json
@ -18,15 +18,15 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"grunt": "~0.4.1",
|
"grunt": "~0.4.1",
|
||||||
"grunt-bump": "~0.0.0",
|
"grunt-bump": "~0.0.2",
|
||||||
"grunt-concurrent": "~0.1.1",
|
"grunt-concurrent": "~0.2.0",
|
||||||
"grunt-contrib-clean": "~0.4.0",
|
"grunt-contrib-clean": "~0.4.1",
|
||||||
"grunt-contrib-coffee": "~0.6.6",
|
"grunt-contrib-coffee": "~0.6.7",
|
||||||
"grunt-contrib-compress": "~0.4.10",
|
"grunt-contrib-compress": "~0.4.10",
|
||||||
"grunt-contrib-concat": "~0.2.0",
|
"grunt-contrib-concat": "~0.2.0",
|
||||||
"grunt-contrib-copy": "~0.4.1",
|
"grunt-contrib-copy": "~0.4.1",
|
||||||
"grunt-contrib-watch": "~0.3.1",
|
"grunt-contrib-watch": "~0.3.1",
|
||||||
"grunt-exec": "~0.4.0"
|
"grunt-shell": "~0.2.2"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@ -1,82 +0,0 @@
|
|||||||
Redirect =
|
|
||||||
image: (boardID, filename) ->
|
|
||||||
# Do not use g.BOARD, the image url can originate from a cross-quote.
|
|
||||||
switch boardID
|
|
||||||
when 'a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg'
|
|
||||||
"//archive.foolz.us/#{boardID}/full_image/#{filename}"
|
|
||||||
when 'u'
|
|
||||||
"//nsfw.foolz.us/#{boardID}/full_image/#{filename}"
|
|
||||||
when 'po'
|
|
||||||
"//archive.thedarkcave.org/#{boardID}/full_image/#{filename}"
|
|
||||||
when 'ck', 'fa', 'lit', 's4s'
|
|
||||||
"//fuuka.warosu.org/#{boardID}/full_image/#{filename}"
|
|
||||||
when 'cgl', 'g', 'mu', 'w'
|
|
||||||
"//rbt.asia/#{boardID}/full_image/#{filename}"
|
|
||||||
when 'an', 'k', 'toy', 'x'
|
|
||||||
"http://archive.heinessen.com/#{boardID}/full_image/#{filename}"
|
|
||||||
when 'c'
|
|
||||||
"//archive.nyafuu.org/#{boardID}/full_image/#{filename}"
|
|
||||||
post: (boardID, postID) ->
|
|
||||||
# XXX foolz had HSTS set for 120 days, which broke XHR+CORS+Redirection when on HTTP.
|
|
||||||
# Remove necessary HTTPS procotol in September 2013.
|
|
||||||
switch boardID
|
|
||||||
when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg'
|
|
||||||
"https://archive.foolz.us/_/api/chan/post/?board=#{boardID}&num=#{postID}"
|
|
||||||
when 'u'
|
|
||||||
"https://nsfw.foolz.us/_/api/chan/post/?board=#{boardID}&num=#{postID}"
|
|
||||||
when 'c', 'int', 'out', 'po'
|
|
||||||
"//archive.thedarkcave.org/_/api/chan/post/?board=#{boardID}&num=#{postID}"
|
|
||||||
# for fuuka-based archives:
|
|
||||||
# https://github.com/eksopl/fuuka/issues/27
|
|
||||||
to: (data) ->
|
|
||||||
{boardID} = data
|
|
||||||
switch boardID
|
|
||||||
when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg'
|
|
||||||
Redirect.path '//archive.foolz.us', 'foolfuuka', data
|
|
||||||
when 'u'
|
|
||||||
Redirect.path '//nsfw.foolz.us', 'foolfuuka', data
|
|
||||||
when 'int', 'out', 'po'
|
|
||||||
Redirect.path '//archive.thedarkcave.org', 'foolfuuka', data
|
|
||||||
when 'ck', 'fa', 'lit', 's4s'
|
|
||||||
Redirect.path '//fuuka.warosu.org', 'fuuka', data
|
|
||||||
when 'diy', 'g', 'sci'
|
|
||||||
Redirect.path '//archive.installgentoo.net', 'fuuka', data
|
|
||||||
when 'cgl', 'mu', 'w'
|
|
||||||
Redirect.path '//rbt.asia', 'fuuka', data
|
|
||||||
when 'an', 'fit', 'k', 'mlp', 'r9k', 'toy', 'x'
|
|
||||||
Redirect.path 'http://archive.heinessen.com', 'fuuka', data
|
|
||||||
when 'c'
|
|
||||||
Redirect.path '//archive.nyafuu.org', 'fuuka', data
|
|
||||||
else
|
|
||||||
if data.threadID then "//boards.4chan.org/#{boardID}/" else ''
|
|
||||||
path: (base, archiver, data) ->
|
|
||||||
if data.isSearch
|
|
||||||
{boardID, type, value} = data
|
|
||||||
type = if type is 'name'
|
|
||||||
'username'
|
|
||||||
else if type is 'MD5'
|
|
||||||
'image'
|
|
||||||
else
|
|
||||||
type
|
|
||||||
value = encodeURIComponent value
|
|
||||||
return if archiver is 'foolfuuka'
|
|
||||||
"#{base}/#{boardID}/search/#{type}/#{value}"
|
|
||||||
else if type is 'image'
|
|
||||||
"#{base}/#{boardID}/?task=search2&search_media_hash=#{value}"
|
|
||||||
else
|
|
||||||
"#{base}/#{boardID}/?task=search2&search_#{type}=#{value}"
|
|
||||||
|
|
||||||
{boardID, threadID, postID} = data
|
|
||||||
# keep the number only if the location.hash was sent f.e.
|
|
||||||
path = if threadID
|
|
||||||
"#{boardID}/thread/#{threadID}"
|
|
||||||
else
|
|
||||||
"#{boardID}/post/#{postID}"
|
|
||||||
if archiver is 'foolfuuka'
|
|
||||||
path += '/'
|
|
||||||
if threadID and postID
|
|
||||||
path += if archiver is 'foolfuuka'
|
|
||||||
"##{postID}"
|
|
||||||
else
|
|
||||||
"#p#{postID}"
|
|
||||||
"#{base}/#{path}"
|
|
||||||
@ -9,18 +9,14 @@ Config =
|
|||||||
false
|
false
|
||||||
'Link to external catalog instead of the internal one.'
|
'Link to external catalog instead of the internal one.'
|
||||||
]
|
]
|
||||||
'Enable 4chan\'s Extension': [
|
|
||||||
false
|
|
||||||
'Compatibility between <%= meta.name %> and 4chan\'s inline extension is NOT guaranteed.'
|
|
||||||
]
|
|
||||||
'Fixed Header': [
|
|
||||||
false
|
|
||||||
'Mayhem X\'s Fixed Header (kinda).'
|
|
||||||
]
|
|
||||||
'Custom Board Navigation': [
|
'Custom Board Navigation': [
|
||||||
false
|
false
|
||||||
'Show custom links instead of the full board list.'
|
'Show custom links instead of the full board list.'
|
||||||
]
|
]
|
||||||
|
'QR Shortcut': [
|
||||||
|
false,
|
||||||
|
'Adds a small [QR] link in the header.'
|
||||||
|
]
|
||||||
'Announcement Hiding': [
|
'Announcement Hiding': [
|
||||||
true
|
true
|
||||||
'Add button to hide 4chan announcements.'
|
'Add button to hide 4chan announcements.'
|
||||||
@ -105,10 +101,6 @@ Config =
|
|||||||
true
|
true
|
||||||
'Add buttons to hide single replies.'
|
'Add buttons to hide single replies.'
|
||||||
]
|
]
|
||||||
'Hiding Buttons': [
|
|
||||||
true
|
|
||||||
'Add buttons to hide threads / replies, in addition to menu links.'
|
|
||||||
]
|
|
||||||
'Stubs': [
|
'Stubs': [
|
||||||
true
|
true
|
||||||
'Show stubs of hidden threads / replies.'
|
'Show stubs of hidden threads / replies.'
|
||||||
@ -145,7 +137,7 @@ Config =
|
|||||||
]
|
]
|
||||||
'Fappe Tyme': [
|
'Fappe Tyme': [
|
||||||
false
|
false
|
||||||
'Hide posts without images when toggled.'
|
'Hide posts without images when toggled. *hint* *hint*'
|
||||||
]
|
]
|
||||||
|
|
||||||
'Menu':
|
'Menu':
|
||||||
@ -153,6 +145,10 @@ Config =
|
|||||||
true
|
true
|
||||||
'Add a drop-down menu to posts.'
|
'Add a drop-down menu to posts.'
|
||||||
]
|
]
|
||||||
|
'Report Link': [
|
||||||
|
true
|
||||||
|
'Add a report link to the menu.'
|
||||||
|
]
|
||||||
'Thread Hiding Link': [
|
'Thread Hiding Link': [
|
||||||
true
|
true
|
||||||
'Add a link to hide entire threads.'
|
'Add a link to hide entire threads.'
|
||||||
@ -160,19 +156,17 @@ Config =
|
|||||||
'Reply Hiding Link': [
|
'Reply Hiding Link': [
|
||||||
true
|
true
|
||||||
'Add a link to hide single replies.'
|
'Add a link to hide single replies.'
|
||||||
]
|
|
||||||
'Report Link': [
|
|
||||||
true
|
|
||||||
'Add a report link to the menu.'
|
|
||||||
]
|
]
|
||||||
'Delete Link': [
|
'Delete Link': [
|
||||||
true
|
true
|
||||||
'Add post and image deletion links to the menu.'
|
'Add post and image deletion links to the menu.'
|
||||||
]
|
]
|
||||||
|
<% if (type === 'crx') { %>
|
||||||
'Download Link': [
|
'Download Link': [
|
||||||
true
|
true
|
||||||
'Add a download with original filename link to the menu. Chrome-only currently.'
|
'Add a download with original filename link to the menu. Chrome-only currently.'
|
||||||
]
|
]
|
||||||
|
<% } %>
|
||||||
'Archive Link': [
|
'Archive Link': [
|
||||||
true
|
true
|
||||||
'Add an archive link to the menu.'
|
'Add an archive link to the menu.'
|
||||||
@ -187,6 +181,10 @@ Config =
|
|||||||
true
|
true
|
||||||
'Show the unread posts count in the tab title.'
|
'Show the unread posts count in the tab title.'
|
||||||
]
|
]
|
||||||
|
'Hide Unread Count at (0)': [
|
||||||
|
false
|
||||||
|
'Hide the unread posts count when it reaches 0.'
|
||||||
|
]
|
||||||
'Unread Tab Icon': [
|
'Unread Tab Icon': [
|
||||||
true
|
true
|
||||||
'Show a different favicon when there are unread posts.'
|
'Show a different favicon when there are unread posts.'
|
||||||
@ -195,6 +193,10 @@ Config =
|
|||||||
true
|
true
|
||||||
'Show a line to distinguish read posts from unread ones.'
|
'Show a line to distinguish read posts from unread ones.'
|
||||||
]
|
]
|
||||||
|
'Scroll to Last Read Post': [
|
||||||
|
true
|
||||||
|
'Scroll back to the last read post when reopening a thread.'
|
||||||
|
]
|
||||||
'Thread Excerpt': [
|
'Thread Excerpt': [
|
||||||
true
|
true
|
||||||
'Show an excerpt of the thread in the tab title.'
|
'Show an excerpt of the thread in the tab title.'
|
||||||
@ -78,7 +78,7 @@ body > a[style="cursor: pointer; float: right;"]::after {
|
|||||||
#boardNavDesktopFoot {
|
#boardNavDesktopFoot {
|
||||||
top: 16px !important;
|
top: 16px !important;
|
||||||
}
|
}
|
||||||
#{if _conf['Boards Navigation'] is 'top' or _conf['Boards Navigation'] is 'sticky top' then '#boardNavDesktop' else if _conf['Pagination'] is 'top' or _conf['Pagination'] is 'sticky top' then '.pagelist'} {
|
#{if _conf['Boards Navigation'] is 'top' or _conf['Boards Navigation'] is 'sticky top' then '#header-bar' else if _conf['Pagination'] is 'top' or _conf['Pagination'] is 'sticky top' then '.pagelist'} {
|
||||||
#{if _conf['4chan SS Navigation']
|
#{if _conf['4chan SS Navigation']
|
||||||
"padding-#{align}: #{iconOffset}px;"
|
"padding-#{align}: #{iconOffset}px;"
|
||||||
else
|
else
|
||||||
|
|||||||
@ -78,7 +78,7 @@ div.navLinks > a:first-of-type::after {
|
|||||||
width: #{233 + Style.sidebarOffset.W}px !important;
|
width: #{233 + Style.sidebarOffset.W}px !important;
|
||||||
#{align}: 18px !important;
|
#{align}: 18px !important;
|
||||||
}
|
}
|
||||||
#{if _conf['Boards Navigation'] is 'top' or _conf['Boards Navigation'] is 'sticky top' then '#boardNavDesktop' else if _conf['Pagination'] is 'top' or _conf['Pagination'] is 'sticky top' then '.pagelist'} {
|
#{if _conf['Boards Navigation'] is 'top' or _conf['Boards Navigation'] is 'sticky top' then '#header-bar' else if _conf['Pagination'] is 'top' or _conf['Pagination'] is 'sticky top' then '.pagelist'} {
|
||||||
#{if _conf['4chan SS Navigation']
|
#{if _conf['4chan SS Navigation']
|
||||||
"padding-#{align}: #{iconOffset}px;"
|
"padding-#{align}: #{iconOffset}px;"
|
||||||
else
|
else
|
||||||
|
|||||||
@ -146,7 +146,7 @@ hr {
|
|||||||
margin: 1.5px;
|
margin: 1.5px;
|
||||||
}
|
}
|
||||||
/* Header */
|
/* Header */
|
||||||
#boardNavDesktop {
|
#header-bar {
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -168,34 +168,34 @@ else "
|
|||||||
" else ""}
|
" else ""}
|
||||||
text-align: #{_conf["Navigation Alignment"]};
|
text-align: #{_conf["Navigation Alignment"]};
|
||||||
}
|
}
|
||||||
.fixed #boardNavDesktop {
|
.fixed #header-bar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
}
|
}
|
||||||
.top #boardNavDesktop {
|
.top #header-bar {
|
||||||
top: 0;
|
top: 0;
|
||||||
border-top-width: 0;
|
border-top-width: 0;
|
||||||
#{if _conf["Rounded Edges"] then "border-radius: 0 0 3px 3px;" else ""}"
|
#{if _conf["Rounded Edges"] then "border-radius: 0 0 3px 3px;" else ""}"
|
||||||
}
|
}
|
||||||
.fixed.bottom #boardNavDesktop {
|
.fixed.bottom #header-bar {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
border-bottom-width: 0;
|
border-bottom-width: 0;
|
||||||
#{if _conf["Rounded Edges"] then "border-radius: 3px 3px 0 0;" else ""}"
|
#{if _conf["Rounded Edges"] then "border-radius: 3px 3px 0 0;" else ""}"
|
||||||
}
|
}
|
||||||
.hide #boardNavDesktop {
|
.hide #header-bar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 110%;
|
top: 110%;
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
}
|
}
|
||||||
/* Header Autohide */
|
/* Header Autohide */
|
||||||
.fixed #boardNavDesktop.autohide:not(:hover) {
|
.fixed #header-bar.autohide:not(:hover) {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
transition: all .8s .6s cubic-bezier(.55, .055, .675, .19);
|
transition: all .8s .6s cubic-bezier(.55, .055, .675, .19);
|
||||||
}
|
}
|
||||||
.fixed.top #boardNavDesktop.autohide:not(:hover) {
|
.fixed.top #header-bar.autohide:not(:hover) {
|
||||||
margin-bottom: -1em;
|
margin-bottom: -1em;
|
||||||
#{agent}transform: translateY(-100%);
|
#{agent}transform: translateY(-100%);
|
||||||
}
|
}
|
||||||
.fixed.bottom #boardNavDesktop.autohide:not(:hover) {
|
.fixed.bottom #header-bar.autohide:not(:hover) {
|
||||||
#{agent}transform: translateY(100%);
|
#{agent}transform: translateY(100%);
|
||||||
}
|
}
|
||||||
#toggle-header-bar {
|
#toggle-header-bar {
|
||||||
@ -204,22 +204,22 @@ else "
|
|||||||
height: 10px;
|
height: 10px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
#boardNavDesktop #toggle-header-bar {
|
#header-bar #toggle-header-bar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.fixed #boardNavDesktop #toggle-header-bar {
|
.fixed #header-bar #toggle-header-bar {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.fixed #boardNavDesktop #toggle-header-bar {
|
.fixed #header-bar #toggle-header-bar {
|
||||||
cursor: n-resize;
|
cursor: n-resize;
|
||||||
}
|
}
|
||||||
.fixed.top boardNavDesktop #toggle-header-bar {
|
.fixed.top header-bar #toggle-header-bar {
|
||||||
top: 100%;
|
top: 100%;
|
||||||
}
|
}
|
||||||
.fixed.bottom #boardNavDesktop #toggle-header-bar {
|
.fixed.bottom #header-bar #toggle-header-bar {
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
}
|
}
|
||||||
.fixed #boardNavDesktop #header-bar.autohide #toggle-header-bar {
|
.fixed #header-bar #header-bar.autohide #toggle-header-bar {
|
||||||
cursor: s-resize;
|
cursor: s-resize;
|
||||||
}
|
}
|
||||||
/* Notifications */
|
/* Notifications */
|
||||||
@ -345,7 +345,7 @@ else "
|
|||||||
.pagelist div {
|
.pagelist div {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
#boardNavDesktop a {
|
#header-bar a {
|
||||||
font-size: #{parseInt(_conf["Font Size"], 10)}px;
|
font-size: #{parseInt(_conf["Font Size"], 10)}px;
|
||||||
}
|
}
|
||||||
#{if _conf["Hide Navigation Decorations"] then "
|
#{if _conf["Hide Navigation Decorations"] then "
|
||||||
|
|||||||
@ -173,7 +173,7 @@ textarea.field:focus {
|
|||||||
background: #{theme["Highlighted Reply Background"]};
|
background: #{theme["Highlighted Reply Background"]};
|
||||||
border: 1px solid #{theme["Highlighted Reply Border"]};
|
border: 1px solid #{theme["Highlighted Reply Border"]};
|
||||||
}
|
}
|
||||||
#boardNavDesktop,
|
#header-bar,
|
||||||
.pagelist {
|
.pagelist {
|
||||||
background: #{theme["Navigation Background"]};
|
background: #{theme["Navigation Background"]};
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
@ -286,7 +286,7 @@ textarea {
|
|||||||
border: 1px solid #{theme["Buttons Border"]};
|
border: 1px solid #{theme["Buttons Border"]};
|
||||||
}
|
}
|
||||||
.pages a,
|
.pages a,
|
||||||
#boardNavDesktop a {
|
#header-bar a {
|
||||||
color: #{theme["Navigation Links"]};
|
color: #{theme["Navigation Links"]};
|
||||||
}
|
}
|
||||||
input[type=checkbox]:checked + .rice {
|
input[type=checkbox]:checked + .rice {
|
||||||
@ -331,7 +331,7 @@ a .postertrip:hover,
|
|||||||
a:hover {
|
a:hover {
|
||||||
color: #{theme["Hovered Links"]};
|
color: #{theme["Hovered Links"]};
|
||||||
}
|
}
|
||||||
#boardNavDesktop a:hover,
|
#header-bar a:hover,
|
||||||
#boardTitle a:hover {
|
#boardTitle a:hover {
|
||||||
color: #{theme["Hovered Navigation Links"]};
|
color: #{theme["Hovered Navigation Links"]};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -127,7 +127,7 @@ ThreadHiding =
|
|||||||
ThreadHiding.saveHiddenState thread
|
ThreadHiding.saveHiddenState thread
|
||||||
|
|
||||||
hide: (thread, makeStub=Conf['Stubs']) ->
|
hide: (thread, makeStub=Conf['Stubs']) ->
|
||||||
return if thread.hidden
|
return if thread.isHidden
|
||||||
{OP} = thread
|
{OP} = thread
|
||||||
threadRoot = OP.nodes.root.parentNode
|
threadRoot = OP.nodes.root.parentNode
|
||||||
threadRoot.hidden = thread.isHidden = true
|
threadRoot.hidden = thread.isHidden = true
|
||||||
@ -175,7 +175,7 @@ ImageExpand =
|
|||||||
$.event 'AddMenuEntry',
|
$.event 'AddMenuEntry',
|
||||||
type: 'header'
|
type: 'header'
|
||||||
el: el
|
el: el
|
||||||
order: 80
|
order: 105
|
||||||
subEntries: subEntries
|
subEntries: subEntries
|
||||||
|
|
||||||
createSubEntry: (type, config) ->
|
createSubEntry: (type, config) ->
|
||||||
@ -1,14 +1,7 @@
|
|||||||
DownloadLink =
|
DownloadLink =
|
||||||
init: ->
|
init: ->
|
||||||
<% if (type === 'userscript') { %>
|
|
||||||
# Firefox won't let us download cross-domain content.
|
|
||||||
return
|
|
||||||
<% } %>
|
|
||||||
return if g.VIEW is 'catalog' or !Conf['Menu'] or !Conf['Download Link']
|
return if g.VIEW is 'catalog' or !Conf['Menu'] or !Conf['Download Link']
|
||||||
|
|
||||||
# Test for download feature support.
|
|
||||||
return unless 'download' of $.el 'a'
|
|
||||||
|
|
||||||
a = $.el 'a',
|
a = $.el 'a',
|
||||||
className: 'download-link'
|
className: 'download-link'
|
||||||
textContent: 'Download file'
|
textContent: 'Download file'
|
||||||
@ -12,6 +12,7 @@ PSAHiding =
|
|||||||
|
|
||||||
PSAHiding.btn = btn = $.el 'a',
|
PSAHiding.btn = btn = $.el 'a',
|
||||||
title: 'Toggle announcement.'
|
title: 'Toggle announcement.'
|
||||||
|
innerHTML: '<span></span>'
|
||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
textContent: '[ - ]'
|
textContent: '[ - ]'
|
||||||
$.on btn, 'click', PSAHiding.toggle
|
$.on btn, 'click', PSAHiding.toggle
|
||||||
@ -2,28 +2,33 @@ CatalogLinks =
|
|||||||
init: ->
|
init: ->
|
||||||
$.ready @ready
|
$.ready @ready
|
||||||
return unless Conf['Catalog Links']
|
return unless Conf['Catalog Links']
|
||||||
el = $.el 'a',
|
el = $.el 'label',
|
||||||
id: 'toggleCatalog'
|
id: 'toggleCatalog'
|
||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
className: if Conf['Header catalog links'] then 'disabled' else ''
|
innerHTML: "<input type=checkbox #{if Conf['Header catalog links'] then 'checked' else ''}>Catalog"
|
||||||
textContent: 'Catalog'
|
|
||||||
title: "Turn catalog links #{if Conf['Header catalog links'] then 'off' else 'on'}."
|
title: "Turn catalog links #{if Conf['Header catalog links'] then 'off' else 'on'}."
|
||||||
$.on el, 'click', @toggle
|
|
||||||
|
|
||||||
Header.addShortcut el
|
input = $ 'input', el
|
||||||
|
$.on input, 'change', @toggle
|
||||||
|
$.sync 'Header catalog links', CatalogLinks.set
|
||||||
|
|
||||||
$.asap (-> d.body), ->
|
$.event 'AddMenuEntry',
|
||||||
return unless Main.isThisPageLegit()
|
type: 'header'
|
||||||
# Wait for #boardNavMobile instead of #boardNavDesktop,
|
el: el
|
||||||
# it might be incomplete otherwise.
|
order: 95
|
||||||
$.asap (-> $.id 'boardNavMobile'), ->
|
|
||||||
# Set links on load.
|
$.on d, '4chanXInitFinished', ->
|
||||||
CatalogLinks.toggle.call el
|
# Set links on load.
|
||||||
|
CatalogLinks.set Conf['Header catalog links']
|
||||||
|
|
||||||
toggle: ->
|
toggle: ->
|
||||||
$.set 'Header catalog links', useCatalog = @className is 'disabled'
|
$.event 'CloseMenu'
|
||||||
$.toggleClass @, 'disabled'
|
$.set 'Header catalog links', useCatalog = @checked
|
||||||
for a in $$ 'a', $.id('boardNavDesktop')
|
CatalogLinks.set useCatalog
|
||||||
|
|
||||||
|
set: (useCatalog) ->
|
||||||
|
path = if useCatalog then 'catalog' else ''
|
||||||
|
for a in $$ 'a', $.id('board-list')
|
||||||
board = a.pathname.split('/')[1]
|
board = a.pathname.split('/')[1]
|
||||||
continue if ['f', 'status', '4chan'].contains(board) or !board
|
continue if ['f', 'status', '4chan'].contains(board) or !board
|
||||||
if Conf['External Catalog']
|
if Conf['External Catalog']
|
||||||
@ -32,9 +37,9 @@ CatalogLinks =
|
|||||||
else
|
else
|
||||||
"//boards.4chan.org/#{board}/"
|
"//boards.4chan.org/#{board}/"
|
||||||
else
|
else
|
||||||
a.pathname = "/#{board}/#{if useCatalog then 'catalog' else ''}"
|
a.pathname = "/#{board}/#{path}"
|
||||||
a.title = if useCatalog then "#{a.title} - Catalog" else a.title.replace(/\ -\ Catalog$/, '')
|
a.title = if useCatalog then "#{a.title} - Catalog" else a.title.replace(/\ -\ Catalog$/, '')
|
||||||
@title = "Turn catalog links #{if useCatalog then 'off' else 'on'}."
|
@title = "Turn catalog links #{if useCatalog then 'off' else 'on'}."
|
||||||
|
|
||||||
external: (board) ->
|
external: (board) ->
|
||||||
return (
|
return (
|
||||||
@ -50,4 +55,4 @@ CatalogLinks =
|
|||||||
if catalogLink = ($('.pages.cataloglink a', d.body) or $ '[href=".././catalog"]', d.body)
|
if catalogLink = ($('.pages.cataloglink a', d.body) or $ '[href=".././catalog"]', d.body)
|
||||||
if g.VIEW isnt 'thread'
|
if g.VIEW isnt 'thread'
|
||||||
$.add d.body, catalogLink
|
$.add d.body, catalogLink
|
||||||
catalogLink.id = 'catalog'
|
catalogLink.id = 'catalog'
|
||||||
@ -94,8 +94,4 @@ ExpandThread =
|
|||||||
Main.callbackNodes Post, posts
|
Main.callbackNodes Post, posts
|
||||||
$.after a, nodes
|
$.after a, nodes
|
||||||
|
|
||||||
# Enable 4chan features.
|
Fourchan.parseThread thread.ID, 1, nodes.length
|
||||||
if Conf['Enable 4chan\'s Extension']
|
|
||||||
$.globalEval "Parser.parseThread(#{thread.ID}, 1, #{nodes.length})"
|
|
||||||
else
|
|
||||||
Fourchan.parseThread thread.ID, 1, nodes.length
|
|
||||||
@ -5,34 +5,30 @@ Header =
|
|||||||
id: 'main-menu'
|
id: 'main-menu'
|
||||||
|
|
||||||
@menu = new UI.Menu 'header'
|
@menu = new UI.Menu 'header'
|
||||||
$.on @menuButton, 'click', @menuToggle
|
|
||||||
$.on @toggle, 'mousedown', @toggleBarVisibility
|
|
||||||
$.on window, 'load hashchange', Header.hashScroll
|
|
||||||
|
|
||||||
@positionToggler = $.el 'span',
|
headerToggler = $.el 'label',
|
||||||
textContent: 'Header Position'
|
innerHTML: '<input type=checkbox name="Header auto-hide"> Auto-hide header'
|
||||||
className: 'header-position-link'
|
|
||||||
|
@headerToggler = headerToggler.firstElementChild
|
||||||
|
|
||||||
|
$.on @menuButton, 'click', @menuToggle
|
||||||
|
$.on window, 'load hashchange', Header.hashScroll
|
||||||
|
$.on @headerToggler, 'change', @toggleBarVisibility
|
||||||
|
|
||||||
{createSubEntry} = Header
|
{createSubEntry} = Header
|
||||||
subEntries = []
|
subEntries = []
|
||||||
for setting in ['sticky top', 'sticky bottom', 'top', 'hide']
|
for setting in ['sticky top', 'sticky bottom', 'top', 'hide']
|
||||||
subEntries.push createSubEntry setting
|
subEntries.push createSubEntry setting
|
||||||
|
|
||||||
|
subEntries.push {el: headerToggler}
|
||||||
|
|
||||||
$.event 'AddMenuEntry',
|
$.event 'AddMenuEntry',
|
||||||
type: 'header'
|
type: 'header'
|
||||||
el: @positionToggler
|
el: $.el 'span',
|
||||||
order: 108
|
textContent: 'Header'
|
||||||
|
order: 105
|
||||||
subEntries: subEntries
|
subEntries: subEntries
|
||||||
|
|
||||||
@headerToggler = $.el 'label',
|
|
||||||
innerHTML: "<input type=checkbox #{if Conf['Header auto-hide'] then 'checked' else ''}> Auto-hide header"
|
|
||||||
$.on @headerToggler.firstElementChild, 'change', @toggleBarVisibility
|
|
||||||
|
|
||||||
$.event 'AddMenuEntry',
|
|
||||||
type: 'header'
|
|
||||||
el: @headerToggler
|
|
||||||
order: 109
|
|
||||||
|
|
||||||
$.on d, 'CreateNotification', @createNotification
|
$.on d, 'CreateNotification', @createNotification
|
||||||
|
|
||||||
$.asap (-> d.body), ->
|
$.asap (-> d.body), ->
|
||||||
@ -56,7 +52,7 @@ Header =
|
|||||||
toggle: $.el 'div',
|
toggle: $.el 'div',
|
||||||
id: 'toggle-header-bar'
|
id: 'toggle-header-bar'
|
||||||
|
|
||||||
createSubEntry: (setting)->
|
createSubEntry: (setting) ->
|
||||||
label = $.el 'label',
|
label = $.el 'label',
|
||||||
textContent: "#{setting}"
|
textContent: "#{setting}"
|
||||||
|
|
||||||
@ -66,15 +62,15 @@ Header =
|
|||||||
|
|
||||||
setBoardList: ->
|
setBoardList: ->
|
||||||
Header.nav = nav = $.id 'boardNavDesktop'
|
Header.nav = nav = $.id 'boardNavDesktop'
|
||||||
|
nav.id = 'header-bar'
|
||||||
if a = $ "a[href*='/#{g.BOARD}/']", nav
|
if a = $ "a[href*='/#{g.BOARD}/']", nav
|
||||||
a.className = 'current'
|
a.className = 'current'
|
||||||
|
|
||||||
fullBoardList = $.el 'span',
|
boardList = $.el 'span',
|
||||||
id: 'full-board-list'
|
id: 'board-list'
|
||||||
hidden: true
|
|
||||||
|
|
||||||
customBoardList = $.el 'span',
|
$.add boardList, fullBoardList = $.el 'span',
|
||||||
id: 'custom-board-list'
|
id: 'full-board-list'
|
||||||
|
|
||||||
Header.setBarPosition.call textContent: "#{Conf['Boards Navigation']}"
|
Header.setBarPosition.call textContent: "#{Conf['Boards Navigation']}"
|
||||||
$.sync 'Boards Navigation', Header.changeBarPosition
|
$.sync 'Boards Navigation', Header.changeBarPosition
|
||||||
@ -86,18 +82,25 @@ Header =
|
|||||||
$.add settings, Header.menuButton
|
$.add settings, Header.menuButton
|
||||||
|
|
||||||
$.add fullBoardList, [nav.childNodes...]
|
$.add fullBoardList, [nav.childNodes...]
|
||||||
$.add nav, [customBoardList, fullBoardList, Header.shortcuts, Header.bar, Header.toggle]
|
$.add nav, [boardList, Header.shortcuts, Header.bar, Header.toggle]
|
||||||
|
|
||||||
if Conf['Custom Board Navigation']
|
if Conf['Custom Board Navigation']
|
||||||
|
fullBoardList.hidden = true
|
||||||
|
customBoardList = $.el 'span',
|
||||||
|
id: 'custom-board-list'
|
||||||
|
$.add boardList, customBoardList
|
||||||
|
|
||||||
Header.generateBoardList Conf['boardnav']
|
Header.generateBoardList Conf['boardnav']
|
||||||
$.sync 'boardnav', Header.generateBoardList
|
$.sync 'boardnav', Header.generateBoardList
|
||||||
|
|
||||||
btn = $.el 'span',
|
btn = $.el 'span',
|
||||||
className: 'hide-board-list-button'
|
className: 'hide-board-list-button'
|
||||||
innerHTML: '[<a href=javascript:;> - </a>]\u00A0'
|
innerHTML: '[<a href=javascript:;> - </a>]\u00A0'
|
||||||
$.on btn, 'click', Header.toggleBoardList
|
$.on btn, 'click', Header.toggleBoardList
|
||||||
|
|
||||||
$.prepend fullBoardList, btn
|
$.prepend fullBoardList, btn
|
||||||
|
|
||||||
else
|
else
|
||||||
$.rm $ '#custom-board-list', nav
|
|
||||||
fullBoardList.hidden = false
|
fullBoardList.hidden = false
|
||||||
|
|
||||||
generateBoardList: (text) ->
|
generateBoardList: (text) ->
|
||||||
@ -105,7 +108,7 @@ Header =
|
|||||||
$.rmAll list
|
$.rmAll list
|
||||||
return unless text
|
return unless text
|
||||||
as = $$('#full-board-list a', Header.nav)[0...-2] # ignore the Settings and Home links
|
as = $$('#full-board-list a', Header.nav)[0...-2] # ignore the Settings and Home links
|
||||||
nodes = text.match(/[\w@]+(-(all|title|full|index|catalog|text:"[^"]+"))*|[^\w@]+/g).map (t) ->
|
nodes = text.match(/[\w@]+(-(all|title|replace|full|index|catalog|text:"[^"]+"))*|[^\w@]+/g).map (t) ->
|
||||||
if /^[^\w@]/.test t
|
if /^[^\w@]/.test t
|
||||||
return $.tn t
|
return $.tn t
|
||||||
if /^toggle-all/.test t
|
if /^toggle-all/.test t
|
||||||
@ -124,6 +127,9 @@ Header =
|
|||||||
a = a.cloneNode true
|
a = a.cloneNode true
|
||||||
if /-title/.test t
|
if /-title/.test t
|
||||||
a.textContent = a.title
|
a.textContent = a.title
|
||||||
|
else if /-replace/.test t
|
||||||
|
if $.hasClass a, 'current'
|
||||||
|
a.textContent = a.title
|
||||||
else if /-full/.test t
|
else if /-full/.test t
|
||||||
a.textContent = "/#{board}/ - #{a.title}"
|
a.textContent = "/#{board}/ - #{a.title}"
|
||||||
else if /-(index|catalog|text)/.test t
|
else if /-(index|catalog|text)/.test t
|
||||||
@ -173,7 +179,8 @@ Header =
|
|||||||
$.addClass doc, 'hide'
|
$.addClass doc, 'hide'
|
||||||
|
|
||||||
setBarVisibility: (hide) ->
|
setBarVisibility: (hide) ->
|
||||||
Header.headerToggler.firstElementChild.checked = hide
|
Header.headerToggler.checked = hide
|
||||||
|
$.event 'CloseMenu'
|
||||||
(if hide then $.addClass else $.rmClass) Header.nav, 'autohide'
|
(if hide then $.addClass else $.rmClass) Header.nav, 'autohide'
|
||||||
|
|
||||||
hashScroll: ->
|
hashScroll: ->
|
||||||
@ -193,20 +200,33 @@ Header =
|
|||||||
hide = if @nodeName is 'INPUT'
|
hide = if @nodeName is 'INPUT'
|
||||||
@checked
|
@checked
|
||||||
else
|
else
|
||||||
!$.hasClass Header.nav, 'autohide'
|
!$.hasClass Header.bar, 'autohide'
|
||||||
|
Conf['Header auto-hide'] = hide
|
||||||
|
$.set 'Header auto-hide', hide
|
||||||
Header.setBarVisibility hide
|
Header.setBarVisibility hide
|
||||||
message = if hide
|
message = if hide
|
||||||
'The header bar will automatically hide itself.'
|
'The header bar will automatically hide itself.'
|
||||||
else
|
else
|
||||||
'The header bar will remain visible.'
|
'The header bar will remain visible.'
|
||||||
new Notification 'info', message, 2
|
new Notification 'info', message, 2
|
||||||
$.set 'Header auto-hide', hide
|
|
||||||
|
hashScroll: ->
|
||||||
|
return unless (hash = @location.hash) and post = $.id hash[1..]
|
||||||
|
return if (Get.postFromRoot post).isHidden
|
||||||
|
Header.scrollToPost post
|
||||||
|
|
||||||
|
scrollToPost: (post) ->
|
||||||
|
{top} = post.getBoundingClientRect()
|
||||||
|
if Conf['Boards Navigation'] is 'sticky top'
|
||||||
|
headRect = Header.bar.getBoundingClientRect()
|
||||||
|
top += - headRect.top - headRect.height
|
||||||
|
<% if (type === 'crx') { %>d.body<% } else { %>doc<% } %>.scrollTop += top
|
||||||
|
|
||||||
addShortcut: (el) ->
|
addShortcut: (el) ->
|
||||||
shortcut = $.el 'span',
|
shortcut = $.el 'span',
|
||||||
className: 'shortcut'
|
className: 'shortcut'
|
||||||
$.add shortcut, [$.tn(' ['), el, $.tn(']')]
|
$.add shortcut, [$.tn(' ['), el, $.tn(']')]
|
||||||
$.add Header.shortcuts, shortcut
|
$.prepend Header.shortcuts, shortcut
|
||||||
|
|
||||||
menuToggle: (e) ->
|
menuToggle: (e) ->
|
||||||
Header.menu.toggle e, @, g
|
Header.menu.toggle e, @, g
|
||||||
@ -133,8 +133,10 @@ Keybinds =
|
|||||||
do QR.open
|
do QR.open
|
||||||
if quote
|
if quote
|
||||||
QR.quote.call $ 'input', $('.post.highlight', thread) or thread
|
QR.quote.call $ 'input', $('.post.highlight', thread) or thread
|
||||||
|
|
||||||
do QR.nodes.com.focus
|
do QR.nodes.com.focus
|
||||||
$.rmClass $('.qr-shortcut'), 'disabled'
|
if Conf['QR Shortcut']
|
||||||
|
$.rmClass $('.qr-shortcut'), 'disabled'
|
||||||
|
|
||||||
tags: (tag, ta) ->
|
tags: (tag, ta) ->
|
||||||
value = ta.value
|
value = ta.value
|
||||||
133
src/features/misc/redirection.coffee
Normal file
133
src/features/misc/redirection.coffee
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
Redirect =
|
||||||
|
init: ->
|
||||||
|
$.sync 'archs', @updateArchives
|
||||||
|
|
||||||
|
updateArchives: ->
|
||||||
|
$.get 'archivers', {}, ({archivers}) ->
|
||||||
|
Conf['archivers'] = archivers
|
||||||
|
|
||||||
|
image: (boardID, filename) ->
|
||||||
|
# Do not use g.BOARD, the image url can originate from a cross-quote.
|
||||||
|
switch boardID
|
||||||
|
when 'a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg'
|
||||||
|
"//archive.foolz.us/#{boardID}/full_image/#{filename}"
|
||||||
|
when 'u'
|
||||||
|
"//nsfw.foolz.us/#{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 'ck', 'fa', 'lit', 's4s'
|
||||||
|
"//fuuka.warosu.org/#{boardID}/full_image/#{filename}"
|
||||||
|
when 'cgl', 'g', 'mu', 'w'
|
||||||
|
"//rbt.asia/#{boardID}/full_image/#{filename}"
|
||||||
|
when 'an', 'k', 'toy', 'x'
|
||||||
|
"http://archive.heinessen.com/#{boardID}/full_image/#{filename}"
|
||||||
|
when 'c'
|
||||||
|
"//archive.nyafuu.org/#{boardID}/full_image/#{filename}"
|
||||||
|
|
||||||
|
post: (boardID, postID) ->
|
||||||
|
unless Redirect.post[boardID]?
|
||||||
|
for name, archive of @archiver
|
||||||
|
if archive.type is 'foolfuuka' and archive.boards.contains boardID
|
||||||
|
Redirect.post[boardID] = archive.base
|
||||||
|
break
|
||||||
|
Redirect.post[boardID] or= false
|
||||||
|
|
||||||
|
return if Redirect.post[boardID]
|
||||||
|
"#{Redirect.post[boardID]}/_/api/chan/post/?board=#{boardID}&num=#{postID}"
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
|
select: (board) ->
|
||||||
|
for name, archive of @archiver
|
||||||
|
continue unless archive.boards.contains board
|
||||||
|
name
|
||||||
|
|
||||||
|
to: (data) ->
|
||||||
|
{boardID} = data
|
||||||
|
|
||||||
|
unless (arch = Conf.archivers[boardID])?
|
||||||
|
Conf.archivers[boardID] = arch = @select(boardID)[0]
|
||||||
|
$.set 'archivers', Conf.archivers
|
||||||
|
|
||||||
|
return (if arch and archive = @archiver[arch]
|
||||||
|
Redirect.path archive.base, archive.type, data
|
||||||
|
else if data.threadID
|
||||||
|
"//boards.4chan.org/#{boardID}/"
|
||||||
|
else
|
||||||
|
null)
|
||||||
|
|
||||||
|
archiver:
|
||||||
|
'Foolz':
|
||||||
|
base: 'https://archive.foolz.us'
|
||||||
|
boards: ['a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg']
|
||||||
|
type: 'foolfuuka'
|
||||||
|
'NSFWFoolz':
|
||||||
|
base: 'https://nsfw.foolz.us'
|
||||||
|
boards: ['u']
|
||||||
|
type: 'foolfuuka'
|
||||||
|
'TheDarkCave':
|
||||||
|
base: 'http://archive.thedarkcave.org'
|
||||||
|
boards: ['c', 'int', 'out', 'po']
|
||||||
|
type: 'foolfuuka'
|
||||||
|
'4plebs':
|
||||||
|
base: 'http://archive.4plebs.org'
|
||||||
|
boards: ['hr', 'tg', 'tv', 'x']
|
||||||
|
base: 'foolfuuka'
|
||||||
|
'Warosu':
|
||||||
|
base: '//fuuka.warosu.org'
|
||||||
|
boards: ['cgl', 'ck', 'fa', 'jp', 'lit', 's4s', 'q', 'tg']
|
||||||
|
type: 'fuuka'
|
||||||
|
'RebeccaBlackTech':
|
||||||
|
base: '//rbt.asia'
|
||||||
|
boards: ['an', 'cgl', 'g', 'mu', 'w']
|
||||||
|
type: 'fuuka_mail'
|
||||||
|
'InstallGentoo':
|
||||||
|
base: '//archive.installgentoo.net'
|
||||||
|
boards: ['diy', 'g', 'sci']
|
||||||
|
type: 'fuuka'
|
||||||
|
'Heinessen':
|
||||||
|
base: 'http://archive.heinessen.com'
|
||||||
|
boards: ['an', 'fit', 'k', 'mlp', 'r9k', 'toy', 'x']
|
||||||
|
type: 'fuuka'
|
||||||
|
'Cliche':
|
||||||
|
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
|
||||||
|
{boardID, type, value} = data
|
||||||
|
type = if type is 'name'
|
||||||
|
'username'
|
||||||
|
else if type is 'MD5'
|
||||||
|
'image'
|
||||||
|
else
|
||||||
|
type
|
||||||
|
value = encodeURIComponent value
|
||||||
|
return if archiver is 'foolfuuka'
|
||||||
|
"#{base}/#{boardID}/search/#{type}/#{value}"
|
||||||
|
else if type is 'image'
|
||||||
|
"#{base}/#{boardID}/?task=search2&search_media_hash=#{value}"
|
||||||
|
else
|
||||||
|
"#{base}/#{boardID}/?task=search2&search_#{type}=#{value}"
|
||||||
|
|
||||||
|
{boardID, threadID, postID} = data
|
||||||
|
# keep the number only if the location.hash was sent f.e.
|
||||||
|
path = if threadID
|
||||||
|
"#{boardID}/thread/#{threadID}"
|
||||||
|
else
|
||||||
|
"#{boardID}/post/#{postID}"
|
||||||
|
if archiver is 'foolfuuka'
|
||||||
|
path += '/'
|
||||||
|
if threadID and postID
|
||||||
|
path += if archiver is 'foolfuuka'
|
||||||
|
"##{postID}"
|
||||||
|
else
|
||||||
|
"#p#{postID}"
|
||||||
|
"#{base}/#{path}"
|
||||||
@ -6,7 +6,4 @@ ThreadExcerpt =
|
|||||||
name: 'Thread Excerpt'
|
name: 'Thread Excerpt'
|
||||||
cb: @node
|
cb: @node
|
||||||
node: ->
|
node: ->
|
||||||
d.title = if (excerpt = Get.threadExcerpt @).length > 80
|
d.title = Get.threadExcerpt @
|
||||||
"#{excerpt[...77]}..."
|
|
||||||
else
|
|
||||||
excerpt
|
|
||||||
@ -290,10 +290,7 @@ ThreadUpdater =
|
|||||||
# Enable 4chan features.
|
# Enable 4chan features.
|
||||||
threadID = ThreadUpdater.thread.ID
|
threadID = ThreadUpdater.thread.ID
|
||||||
{length} = $$ '.thread > .postContainer', ThreadUpdater.root
|
{length} = $$ '.thread > .postContainer', ThreadUpdater.root
|
||||||
if Conf['Enable 4chan\'s Extension']
|
Fourchan.parseThread threadID, length - count, length
|
||||||
$.globalEval "Parser.parseThread(#{threadID}, #{-count})"
|
|
||||||
else
|
|
||||||
Fourchan.parseThread threadID, length - count, length
|
|
||||||
|
|
||||||
$.event 'ThreadUpdate',
|
$.event 'ThreadUpdate',
|
||||||
404: false
|
404: false
|
||||||
@ -26,18 +26,22 @@ Unread =
|
|||||||
$.on d, 'ThreadUpdate', Unread.onUpdate
|
$.on d, 'ThreadUpdate', Unread.onUpdate
|
||||||
$.on d, 'scroll visibilitychange', Unread.read
|
$.on d, 'scroll visibilitychange', Unread.read
|
||||||
$.on d, 'visibilitychange', Unread.setLine if Conf['Unread Line']
|
$.on d, 'visibilitychange', Unread.setLine if Conf['Unread Line']
|
||||||
|
if Conf['Scroll to Last Read Post']
|
||||||
|
$.on window, 'load', (posts) =>
|
||||||
|
Unread.scroll.apply @, posts
|
||||||
|
|
||||||
return unless Conf['Scroll to Last Read Post']
|
scroll: (posts) ->
|
||||||
# Let the header's onload callback handle it.
|
# Let the header's onload callback handle it.
|
||||||
return if (hash = location.hash.match /\d+/) and hash[0] of @posts
|
return if (hash = location.hash.match /\d+/) and hash[0] of @posts
|
||||||
if Unread.posts.length
|
if Unread.posts.length
|
||||||
# Scroll to before the first unread post.
|
# Scroll to before the first unread post.
|
||||||
while root = $.x 'preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root
|
while root = $.x 'preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root
|
||||||
break unless (Get.postFromRoot root).isHidden
|
break unless (Get.postFromRoot root).isHidden
|
||||||
|
return unless root
|
||||||
root.scrollIntoView false
|
root.scrollIntoView false
|
||||||
else if posts.length
|
else if posts.length
|
||||||
# Scroll to the last read post.
|
# Scroll to the last read post.
|
||||||
Header.scrollToPost posts[posts.length - 1].nodes.root
|
Header.scrollToPost (posts[post.length - 1]).nodes.root
|
||||||
|
|
||||||
sync: ->
|
sync: ->
|
||||||
lastReadPost = Unread.db.get
|
lastReadPost = Unread.db.get
|
||||||
@ -98,7 +102,7 @@ Unread =
|
|||||||
break if post.ID > Unread.lastReadPost
|
break if post.ID > Unread.lastReadPost
|
||||||
arr.splice 0, i
|
arr.splice 0, i
|
||||||
|
|
||||||
read: (e) ->
|
read: $.debounce 50, (e) ->
|
||||||
return if d.hidden or !Unread.posts.length
|
return if d.hidden or !Unread.posts.length
|
||||||
height = doc.clientHeight
|
height = doc.clientHeight
|
||||||
{posts} = Unread
|
{posts} = Unread
|
||||||
@ -106,8 +110,8 @@ Unread =
|
|||||||
i = posts.length
|
i = posts.length
|
||||||
|
|
||||||
while post = posts[--i]
|
while post = posts[--i]
|
||||||
{bottom, top} = post.nodes.root.getBoundingClientRect()
|
{bottom} = post.nodes.root.getBoundingClientRect()
|
||||||
if (bottom < height) and (top > 0) # post is completely read
|
if (bottom < height) # post is completely read
|
||||||
ID = post.ID
|
ID = post.ID
|
||||||
posts.remove post
|
posts.remove post
|
||||||
return unless ID
|
return unless ID
|
||||||
@ -136,10 +140,7 @@ Unread =
|
|||||||
count = Unread.posts.length
|
count = Unread.posts.length
|
||||||
|
|
||||||
if Conf['Unread Count']
|
if Conf['Unread Count']
|
||||||
d.title = if g.DEAD
|
d.title = "#{if count or !Conf['Hide Unread Count at (0)'] then "(#{count}) " else ''}#{if g.DEAD then "/#{g.BOARD}/ - 404" else "#{Unread.title}"}"
|
||||||
"(#{Unread.posts.length}) /#{g.BOARD}/ - 404"
|
|
||||||
else
|
|
||||||
"(#{Unread.posts.length}) #{Unread.title}"
|
|
||||||
<% if (type === 'crx') { %>
|
<% if (type === 'crx') { %>
|
||||||
# XXX Chrome bug where it doesn't always update the tab title.
|
# XXX Chrome bug where it doesn't always update the tab title.
|
||||||
# crbug.com/124381
|
# crbug.com/124381
|
||||||
@ -81,7 +81,8 @@ QR =
|
|||||||
QR.cleanNotifications()
|
QR.cleanNotifications()
|
||||||
d.activeElement.blur()
|
d.activeElement.blur()
|
||||||
$.rmClass QR.nodes.el, 'dump'
|
$.rmClass QR.nodes.el, 'dump'
|
||||||
$.toggleClass $('.qr-shortcut'), 'disabled'
|
if Conf['QR Shortcut']
|
||||||
|
$.toggleClass $('.qr-shortcut'), 'disabled'
|
||||||
for i in QR.posts
|
for i in QR.posts
|
||||||
QR.posts[0].rm()
|
QR.posts[0].rm()
|
||||||
QR.cooldown.auto = false
|
QR.cooldown.auto = false
|
||||||
@ -303,7 +304,8 @@ QR =
|
|||||||
QR.selected.save com
|
QR.selected.save com
|
||||||
QR.selected.save thread
|
QR.selected.save thread
|
||||||
|
|
||||||
$.rmClass $('.qr-shortcut'), 'disabled'
|
if Conf['QR Shortcut']
|
||||||
|
$.rmClass $('.qr-shortcut'), 'disabled'
|
||||||
|
|
||||||
characterCount: ->
|
characterCount: ->
|
||||||
counter = QR.nodes.charCount
|
counter = QR.nodes.charCount
|
||||||
@ -850,7 +852,7 @@ QR =
|
|||||||
QR.mimeTypes = mimeTypes.split ', '
|
QR.mimeTypes = mimeTypes.split ', '
|
||||||
# Add empty mimeType to avoid errors with URLs selected in Window's file dialog.
|
# Add empty mimeType to avoid errors with URLs selected in Window's file dialog.
|
||||||
QR.mimeTypes.push ''
|
QR.mimeTypes.push ''
|
||||||
nodes.fileInput.max = $('input[name=MAX_FILE_SIZE]').value
|
nodes.fileInput.max = $('input[name=MAX_FILE_SIZE]').value
|
||||||
<% if (type !== 'userjs') { %>
|
<% if (type !== 'userjs') { %>
|
||||||
# Opera's accept attribute is fucked up
|
# Opera's accept attribute is fucked up
|
||||||
nodes.fileInput.accept = "text/*, #{mimeTypes}"
|
nodes.fileInput.accept = "text/*, #{mimeTypes}"
|
||||||
@ -887,8 +889,8 @@ QR =
|
|||||||
$.on elm, 'blur', QR.focusout
|
$.on elm, 'blur', QR.focusout
|
||||||
$.on elm, 'focus', QR.focusin
|
$.on elm, 'focus', QR.focusin
|
||||||
<% } %>
|
<% } %>
|
||||||
$.on QR.nodes.el, 'focusin', QR.focusin
|
$.on dialog, 'focusin', QR.focusin
|
||||||
$.on QR.nodes.el, 'focusout', QR.focusout
|
$.on dialog, 'focusout', QR.focusout
|
||||||
$.on nodes.autohide, 'change', QR.toggleHide
|
$.on nodes.autohide, 'change', QR.toggleHide
|
||||||
$.on nodes.close, 'click', QR.close
|
$.on nodes.close, 'click', QR.close
|
||||||
$.on nodes.dumpButton, 'click', -> nodes.el.classList.toggle 'dump'
|
$.on nodes.dumpButton, 'click', -> nodes.el.classList.toggle 'dump'
|
||||||
@ -72,22 +72,23 @@ QuoteThreading =
|
|||||||
return false unless Unread.posts.contains(qpost) or ((bottom < height) and (top > 0))
|
return false unless Unread.posts.contains(qpost) or ((bottom < height) and (top > 0))
|
||||||
|
|
||||||
qroot = qpost.nodes.root
|
qroot = qpost.nodes.root
|
||||||
threadContainer = qroot.nextSibling
|
unless $.hasClass qroot, 'threadOP'
|
||||||
if threadContainer?.className isnt 'threadContainer'
|
$.addClass qroot, 'threadOP'
|
||||||
threadContainer = $.el 'div',
|
threadContainer = $.el 'div',
|
||||||
className: 'threadContainer'
|
className: 'threadContainer'
|
||||||
$.after qroot, threadContainer
|
$.after qroot, threadContainer
|
||||||
|
else
|
||||||
|
threadContainer = qroot.nextSibling
|
||||||
|
|
||||||
$.add threadContainer, @nodes.root
|
$.add threadContainer, @nodes.root
|
||||||
return true
|
return true
|
||||||
|
|
||||||
toggle: ->
|
toggle: ->
|
||||||
thread = $ '.thread'
|
thread = $ '.thread'
|
||||||
replies = $$ '.thread > .replyContainer, .threadContainer > .replyContainer', thread
|
replies = $$ '.thread > .replyContainer, .threadContainer > .replyContainer', thread
|
||||||
QuoteThreading.enabled = @checked
|
QuoteThreading.enabled = @checked
|
||||||
if @checked
|
if @checked
|
||||||
nodes = (Get.postFromNode reply for reply in replies)
|
nodes = (Get.postFromNode reply for reply in replies)
|
||||||
Unread.node.call node for node in nodes
|
|
||||||
QuoteThreading.node node for node in nodes
|
QuoteThreading.node node for node in nodes
|
||||||
else
|
else
|
||||||
replies.sort (a, b) ->
|
replies.sort (a, b) ->
|
||||||
@ -98,6 +99,7 @@ QuoteThreading =
|
|||||||
containers = $$ '.threadContainer', thread
|
containers = $$ '.threadContainer', thread
|
||||||
$.rm container for container in containers
|
$.rm container for container in containers
|
||||||
Unread.update true
|
Unread.update true
|
||||||
|
return
|
||||||
|
|
||||||
kb: ->
|
kb: ->
|
||||||
control = $.id 'threadingControl'
|
control = $.id 'threadingControl'
|
||||||
@ -5,6 +5,8 @@ Get =
|
|||||||
OP.info.comment.replace(/\n+/g, ' // ') or
|
OP.info.comment.replace(/\n+/g, ' // ') or
|
||||||
Conf['Anonymize'] and 'Anonymous' or
|
Conf['Anonymize'] and 'Anonymous' or
|
||||||
$('.nameBlock', OP.nodes.info).textContent.trim()
|
$('.nameBlock', OP.nodes.info).textContent.trim()
|
||||||
|
if excerpt.length > 70
|
||||||
|
excerpt = "#{excerpt[...67]}..."
|
||||||
"/#{thread.board}/ - #{excerpt}"
|
"/#{thread.board}/ - #{excerpt}"
|
||||||
postFromRoot: (root) ->
|
postFromRoot: (root) ->
|
||||||
link = $ 'a[title="Highlight this post"]', root
|
link = $ 'a[title="Highlight this post"]', root
|
||||||
|
|||||||
@ -54,14 +54,13 @@ UI = do ->
|
|||||||
menu = @makeMenu()
|
menu = @makeMenu()
|
||||||
currentMenu = menu
|
currentMenu = menu
|
||||||
lastToggledButton = button
|
lastToggledButton = button
|
||||||
|
|
||||||
|
@entries.sort (first, second) ->
|
||||||
|
first.order - second.order
|
||||||
|
|
||||||
for entry in @entries
|
for entry in @entries
|
||||||
@insertEntry entry, menu, data
|
@insertEntry entry, menu, data
|
||||||
|
|
||||||
entry = $ '.entry', menu
|
|
||||||
while prevEntry = @findNextEntry entry, -1
|
|
||||||
entry = prevEntry
|
|
||||||
@focus entry
|
|
||||||
$.on d, 'click', @close
|
$.on d, 'click', @close
|
||||||
$.on d, 'CloseMenu', @close
|
$.on d, 'CloseMenu', @close
|
||||||
Rice.nodes menu
|
Rice.nodes menu
|
||||||
@ -87,6 +86,14 @@ UI = do ->
|
|||||||
style.right = "#{right}px"
|
style.right = "#{right}px"
|
||||||
style.bottom = "#{bottom}px"
|
style.bottom = "#{bottom}px"
|
||||||
style.left = "#{left}px"
|
style.left = "#{left}px"
|
||||||
|
if right
|
||||||
|
$.addClass menu, 'left'
|
||||||
|
|
||||||
|
entry = $ '.entry', menu
|
||||||
|
# We've removed flexbox, so we don't user order anymore.
|
||||||
|
# while prevEntry = @findNextEntry entry, -1
|
||||||
|
# entry = prevEntry
|
||||||
|
@focus entry
|
||||||
|
|
||||||
menu.focus()
|
menu.focus()
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,6 @@ Main =
|
|||||||
flatten null, Config
|
flatten null, Config
|
||||||
for db in DataBoards
|
for db in DataBoards
|
||||||
Conf[db] = boards: {}
|
Conf[db] = boards: {}
|
||||||
|
|
||||||
# Unflattened Config.
|
# Unflattened Config.
|
||||||
$.extend Conf,
|
$.extend Conf,
|
||||||
'userThemes': []
|
'userThemes': []
|
||||||
@ -24,6 +23,7 @@ Main =
|
|||||||
'Enabled Mascots nsfw': []
|
'Enabled Mascots nsfw': []
|
||||||
'Deleted Mascots': []
|
'Deleted Mascots': []
|
||||||
'Hidden Categories': ["Questionable"]
|
'Hidden Categories': ["Questionable"]
|
||||||
|
'archivers': {}
|
||||||
|
|
||||||
$.get Conf, Main.initFeatures
|
$.get Conf, Main.initFeatures
|
||||||
|
|
||||||
@ -93,6 +93,7 @@ Main =
|
|||||||
'Rice': Rice
|
'Rice': Rice
|
||||||
'Banner': Banner
|
'Banner': Banner
|
||||||
'Announcements': GlobalMessage
|
'Announcements': GlobalMessage
|
||||||
|
'Redirection': Redirect
|
||||||
'Header': Header
|
'Header': Header
|
||||||
'Catalog Links': CatalogLinks
|
'Catalog Links': CatalogLinks
|
||||||
'Settings': Settings
|
'Settings': Settings
|
||||||
@ -125,6 +126,7 @@ Main =
|
|||||||
'Time Formatting': Time
|
'Time Formatting': Time
|
||||||
'Relative Post Dates': RelativeDates
|
'Relative Post Dates': RelativeDates
|
||||||
'File Info Formatting': FileInfo
|
'File Info Formatting': FileInfo
|
||||||
|
'Fappe Tyme': FappeTyme
|
||||||
'Sauce': Sauce
|
'Sauce': Sauce
|
||||||
'Image Expansion': ImageExpand
|
'Image Expansion': ImageExpand
|
||||||
'Image Expansion (Menu)': ImageExpand.menu
|
'Image Expansion (Menu)': ImageExpand.menu
|
||||||
@ -7,6 +7,7 @@ Settings =
|
|||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
$.on link, 'click', Settings.open
|
$.on link, 'click', Settings.open
|
||||||
|
|
||||||
|
|
||||||
$.asap (-> d.body), ->
|
$.asap (-> d.body), ->
|
||||||
return unless Main.isThisPageLegit()
|
return unless Main.isThisPageLegit()
|
||||||
# Wait for #boardNavMobile instead of #boardNavDesktop,
|
# Wait for #boardNavMobile instead of #boardNavDesktop,
|
||||||
@ -41,10 +42,10 @@ Settings =
|
|||||||
Settings.addSection 'Sauce', Settings.sauce
|
Settings.addSection 'Sauce', Settings.sauce
|
||||||
Settings.addSection 'Rice', Settings.rice
|
Settings.addSection 'Rice', Settings.rice
|
||||||
Settings.addSection 'Keybinds', Settings.keybinds
|
Settings.addSection 'Keybinds', Settings.keybinds
|
||||||
|
|
||||||
$.on d, 'AddSettingsSection', Settings.addSection
|
$.on d, 'AddSettingsSection', Settings.addSection
|
||||||
$.on d, 'OpenSettings', (e) -> Settings.open e.detail
|
$.on d, 'OpenSettings', (e) -> Settings.open e.detail
|
||||||
|
|
||||||
return if Conf['Enable 4chan\'s Extension']
|
|
||||||
settings = JSON.parse(localStorage.getItem '4chan-settings') or {}
|
settings = JSON.parse(localStorage.getItem '4chan-settings') or {}
|
||||||
return if settings.disableAll
|
return if settings.disableAll
|
||||||
settings.disableAll = true
|
settings.disableAll = true
|
||||||
@ -73,7 +74,7 @@ Settings =
|
|||||||
<div class=credits>
|
<div class=credits>
|
||||||
<a href='<%= meta.page %>' target=_blank><%= meta.name %></a> |
|
<a href='<%= meta.page %>' target=_blank><%= meta.name %></a> |
|
||||||
<a href='<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md' target=_blank>#{g.VERSION}</a> |
|
<a href='<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md' target=_blank>#{g.VERSION}</a> |
|
||||||
<a href='<%= meta.repo %>blob/<%= meta.mainBranch %>/CONTRIBUTING.md#reporting-bugs' target=_blank>Issues</a> |
|
<a href='<%= meta.repo %>blob/<%= meta.mainBranch %>/CONTRIBUTING.md#reporting-bugs-and-suggestions' target=_blank>Issues</a> |
|
||||||
<a href=javascript:; class=close title=Close>×</a>
|
<a href=javascript:; class=close title=Close>×</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
@ -403,6 +404,11 @@ Settings =
|
|||||||
|
|
||||||
rice: (section) ->
|
rice: (section) ->
|
||||||
section.innerHTML = """
|
section.innerHTML = """
|
||||||
|
<fieldset>
|
||||||
|
<legend>Archiver</legend>
|
||||||
|
Select an Archiver for this board:
|
||||||
|
<select name=archiver></select>
|
||||||
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Custom Board Navigation <span class=warning #{if Conf['Custom Board Navigation'] then 'hidden' else ''}>is disabled.</span></legend>
|
<legend>Custom Board Navigation <span class=warning #{if Conf['Custom Board Navigation'] then 'hidden' else ''}>is disabled.</span></legend>
|
||||||
<div><input name=boardnav class=field spellcheck=false></div>
|
<div><input name=boardnav class=field spellcheck=false></div>
|
||||||
@ -416,6 +422,7 @@ Settings =
|
|||||||
</div>
|
</div>
|
||||||
<div>Board link: <code>board</code></div>
|
<div>Board link: <code>board</code></div>
|
||||||
<div>Title link: <code>board-title</code></div>
|
<div>Title link: <code>board-title</code></div>
|
||||||
|
<div>Board link (Replace with title when on that board): <code>board-replace</code></div>
|
||||||
<div>Full text link: <code>board-full</code></div>
|
<div>Full text link: <code>board-full</code></div>
|
||||||
<div>Custom text link: <code>board-text:"VIP Board"</code></div>
|
<div>Custom text link: <code>board-text:"VIP Board"</code></div>
|
||||||
<div>Index-only link: <code>board-index</code></div>
|
<div>Index-only link: <code>board-index</code></div>
|
||||||
@ -479,6 +486,21 @@ Settings =
|
|||||||
else
|
else
|
||||||
'input'
|
'input'
|
||||||
$.on input, event, $.cb.value
|
$.on input, event, $.cb.value
|
||||||
|
|
||||||
|
# Archiver
|
||||||
|
archiver = $ 'select[name=archiver]', section
|
||||||
|
toSelect = Redirect.select g.BOARD.ID
|
||||||
|
toSelect = ['No Archive Available'] unless toSelect[0]
|
||||||
|
|
||||||
|
$.add archiver, $.el('option', {textContent: name}) for name in toSelect
|
||||||
|
|
||||||
|
if toSelect[1]
|
||||||
|
Conf['archivers'][g.BOARD]
|
||||||
|
archiver.value = Conf['archivers'][g.BOARD] or toSelect[0]
|
||||||
|
$.on archiver, 'change', ->
|
||||||
|
Conf['archivers'][g.BOARD] = @value
|
||||||
|
$.set 'archivers', Conf.archivers
|
||||||
|
|
||||||
$.get items, (items) ->
|
$.get items, (items) ->
|
||||||
for key, val of items
|
for key, val of items
|
||||||
input = inputs[key]
|
input = inputs[key]
|
||||||
@ -1096,4 +1118,4 @@ Settings =
|
|||||||
userThemes = item["userThemes"]
|
userThemes = item["userThemes"]
|
||||||
userThemes[@id] = Themes[@id]
|
userThemes[@id] = Themes[@id]
|
||||||
$.set 'userThemes', userThemes
|
$.set 'userThemes', userThemes
|
||||||
$.rm @
|
$.rm @
|
||||||
Loading…
x
Reference in New Issue
Block a user