Merge branch 'v3' into Av2
Conflicts: builds/4chan-X.meta.js css/style.css lib/ui.coffee package.json src/features.coffee src/qr.coffee
This commit is contained in:
commit
30880e499a
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,3 @@
|
|||||||
builds/
|
|
||||||
node_modules/
|
node_modules/
|
||||||
tmp/
|
tmp/
|
||||||
4chan_x.user.js
|
4chan_x.user.js
|
||||||
|
|||||||
8996
4chan-X.user.js
8996
4chan-X.user.js
File diff suppressed because one or more lines are too long
16
CHANGELOG.md
16
CHANGELOG.md
@ -1,3 +1,17 @@
|
|||||||
|
- Fix resurrecting dead quotelinks on HTTP.
|
||||||
|
|
||||||
|
### 3.0.6 - *2013-04-14*
|
||||||
|
|
||||||
|
- Fix regression concerning thread selection when quoting on the index.
|
||||||
|
|
||||||
|
### 3.0.5 - *2013-04-14*
|
||||||
|
|
||||||
|
- `Scroll to Last Read Post` is now optional, enabled by default.
|
||||||
|
- The QR won't auto-hide when auto-hide is enabled and one of its input is focused. Doesn't work on Firefox.
|
||||||
|
- Added the `Remember QR Size` setting back in, disabled by default. Only on Firefox.
|
||||||
|
- Fix QR remembering the file spoiler state when it shouldn't.
|
||||||
|
- Fix QR cooldown in Opera.
|
||||||
|
|
||||||
### 3.0.4 - *2013-04-11*
|
### 3.0.4 - *2013-04-11*
|
||||||
|
|
||||||
- More minor fixes.
|
- More minor fixes.
|
||||||
@ -9,7 +23,7 @@
|
|||||||
### 3.0.2 - *2013-04-09*
|
### 3.0.2 - *2013-04-09*
|
||||||
|
|
||||||
- Added a setting in the Header's menu to move it at the bottom of the screen.
|
- Added a setting in the Header's menu to move it at the bottom of the screen.
|
||||||
- Added Cooldown setting back in.
|
- Added the `Cooldown` setting back in.
|
||||||
- Fixed the Header going above posts when following quotelinks for example.
|
- Fixed the Header going above posts when following quotelinks for example.
|
||||||
- Fixed a bug where dead quotelinks would disappear.
|
- Fixed a bug where dead quotelinks would disappear.
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
module.exports = (grunt) ->
|
module.exports = (grunt) ->
|
||||||
|
|
||||||
pkg = grunt.file.readJSON 'package.json'
|
pkg = grunt.file.readJSON 'package.json'
|
||||||
|
concatOptions =
|
||||||
|
process:
|
||||||
|
data: pkg
|
||||||
|
|
||||||
# Project configuration.
|
# Project configuration.
|
||||||
grunt.initConfig
|
grunt.initConfig
|
||||||
pkg: pkg
|
pkg: pkg
|
||||||
concat:
|
concat:
|
||||||
coffee:
|
coffee:
|
||||||
options:
|
options: concatOptions
|
||||||
process:
|
|
||||||
data: pkg
|
|
||||||
src: [
|
src: [
|
||||||
'src/config.coffee'
|
'src/config.coffee'
|
||||||
'src/globals.coffee'
|
'src/globals.coffee'
|
||||||
@ -27,9 +28,7 @@ module.exports = (grunt) ->
|
|||||||
dest: 'tmp/script.coffee'
|
dest: 'tmp/script.coffee'
|
||||||
|
|
||||||
crx:
|
crx:
|
||||||
options:
|
options: concatOptions
|
||||||
process:
|
|
||||||
data: pkg
|
|
||||||
files:
|
files:
|
||||||
'builds/crx/manifest.json': 'src/manifest.json'
|
'builds/crx/manifest.json': 'src/manifest.json'
|
||||||
'builds/crx/script.js': [
|
'builds/crx/script.js': [
|
||||||
@ -38,9 +37,7 @@ module.exports = (grunt) ->
|
|||||||
]
|
]
|
||||||
|
|
||||||
userjs:
|
userjs:
|
||||||
options:
|
options: concatOptions
|
||||||
process:
|
|
||||||
data: pkg
|
|
||||||
src: [
|
src: [
|
||||||
'src/metadata.js'
|
'src/metadata.js'
|
||||||
'src/banner.js'
|
'src/banner.js'
|
||||||
@ -49,12 +46,10 @@ module.exports = (grunt) ->
|
|||||||
dest: 'builds/<%= pkg.name %>.js'
|
dest: 'builds/<%= pkg.name %>.js'
|
||||||
|
|
||||||
userscript:
|
userscript:
|
||||||
options:
|
options: concatOptions
|
||||||
process:
|
|
||||||
data: pkg
|
|
||||||
files:
|
files:
|
||||||
'<%= pkg.name %>.meta.js': 'src/metadata.js'
|
'builds/<%= pkg.name %>.meta.js': 'src/metadata.js'
|
||||||
'<%= pkg.name %>.user.js': [
|
'builds/<%= pkg.name %>.user.js': [
|
||||||
'src/metadata.js'
|
'src/metadata.js'
|
||||||
'src/banner.js'
|
'src/banner.js'
|
||||||
'tmp/script.js'
|
'tmp/script.js'
|
||||||
@ -170,22 +165,29 @@ module.exports = (grunt) ->
|
|||||||
|
|
||||||
grunt.registerTask 'patch', [
|
grunt.registerTask 'patch', [
|
||||||
'bump'
|
'bump'
|
||||||
|
'reloadPkh'
|
||||||
'updcl:3'
|
'updcl:3'
|
||||||
]
|
]
|
||||||
|
|
||||||
grunt.registerTask 'minor', [
|
grunt.registerTask 'minor', [
|
||||||
'bump:minor'
|
'bump:minor'
|
||||||
|
'reloadPkh'
|
||||||
'updcl:2'
|
'updcl:2'
|
||||||
]
|
]
|
||||||
|
|
||||||
grunt.registerTask 'major', [
|
grunt.registerTask 'major', [
|
||||||
'bump:major'
|
'bump:major'
|
||||||
|
'reloadPkh'
|
||||||
'updcl:1'
|
'updcl:1'
|
||||||
]
|
]
|
||||||
|
|
||||||
grunt.registerTask 'updcl', 'Update the changelog', (i) ->
|
grunt.registerTask 'reloadPkg', 'Reload the package', ->
|
||||||
# Update the `pkg` object with the new version.
|
# Update the `pkg` object with the new version.
|
||||||
pkg = grunt.file.readJSON('package.json');
|
pkg = grunt.file.readJSON('package.json')
|
||||||
|
concatOptions.process.data = pkg
|
||||||
|
grunt.log.ok('pkg reloaded.')
|
||||||
|
|
||||||
|
grunt.registerTask 'updcl', 'Update the changelog', (i) ->
|
||||||
# i is the number of #s for markdown.
|
# i is the number of #s for markdown.
|
||||||
version = []
|
version = []
|
||||||
version.length = +i + 1
|
version.length = +i + 1
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
13596
builds/appchan-x.user.js
Normal file
13596
builds/appchan-x.user.js
Normal file
File diff suppressed because one or more lines are too long
BIN
builds/crx/icon128.png
Normal file
BIN
builds/crx/icon128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 255 B |
BIN
builds/crx/icon16.png
Normal file
BIN
builds/crx/icon16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 185 B |
BIN
builds/crx/icon48.png
Normal file
BIN
builds/crx/icon48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 238 B |
21
builds/crx/manifest.json
Normal file
21
builds/crx/manifest.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "appchan x",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"manifest_version": 2,
|
||||||
|
"description": "The most comprehensive 4chan userscript.",
|
||||||
|
"icons": {
|
||||||
|
"16": "icon16.png",
|
||||||
|
"48": "icon48.png",
|
||||||
|
"128": "icon128.png"
|
||||||
|
},
|
||||||
|
"content_scripts": [{
|
||||||
|
"js": ["script.js"],
|
||||||
|
"matches": ["*://api.4chan.org/*","*://boards.4chan.org/*","*://images.4chan.org/*","*://sys.4chan.org/*"],
|
||||||
|
"run_at": "document_start"
|
||||||
|
}],
|
||||||
|
"homepage_url": "http://zixaphir.github.com/appchan-x/",
|
||||||
|
"minimum_chrome_version": "26",
|
||||||
|
"permissions": [
|
||||||
|
"storage"
|
||||||
|
]
|
||||||
|
}
|
||||||
13521
builds/crx/script.js
Normal file
13521
builds/crx/script.js
Normal file
File diff suppressed because one or more lines are too long
@ -6,9 +6,7 @@ body > div.navLinks > a:first-of-type::after,
|
|||||||
#{if Conf['Announcements'] is 'slideout' then '#globalMessage::after,' else ''}
|
#{if Conf['Announcements'] is 'slideout' then '#globalMessage::after,' else ''}
|
||||||
#boardNavDesktopFoot::after,
|
#boardNavDesktopFoot::after,
|
||||||
body > a[style="cursor: pointer; float: right;"]::after,
|
body > a[style="cursor: pointer; float: right;"]::after,
|
||||||
#imgControls .expand-all-shortcut,
|
#img-controls,
|
||||||
#imgControls .contract-all-shortcut,
|
|
||||||
#imgControls .menu-button,
|
|
||||||
#catalog::after,
|
#catalog::after,
|
||||||
#fappeTyme {
|
#fappeTyme {
|
||||||
z-index: 18;
|
z-index: 18;
|
||||||
@ -34,9 +32,7 @@ body > div.navLinks > a:first-of-type:hover,
|
|||||||
#{if Conf['Announcements'] is 'slideout' then '#globalMessage:hover,' else ''}
|
#{if Conf['Announcements'] is 'slideout' then '#globalMessage:hover,' else ''}
|
||||||
#boardNavDesktopFoot:hover,
|
#boardNavDesktopFoot:hover,
|
||||||
body > a[style="cursor: pointer; float: right;"]:hover,
|
body > a[style="cursor: pointer; float: right;"]:hover,
|
||||||
#imgControls .expand-all-shortcut,
|
#img-controls,
|
||||||
#imgControls .contract-all-shortcut,
|
|
||||||
#imgControls .menu-button,
|
|
||||||
#catalog:hover {
|
#catalog:hover {
|
||||||
z-index: 17;
|
z-index: 17;
|
||||||
}
|
}
|
||||||
@ -62,8 +58,7 @@ body > a[style="cursor: pointer; float: right;"]::after {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-position: 0 -75px;
|
background-position: 0 -75px;
|
||||||
}
|
}
|
||||||
#imgControls .expand-all-shortcut,
|
#img-controls {
|
||||||
#imgControls .contract-all-shortcut {
|
|
||||||
background-position: 0 -90px;
|
background-position: 0 -90px;
|
||||||
}
|
}
|
||||||
#navtopright .exlinksOptionsLink::after {
|
#navtopright .exlinksOptionsLink::after {
|
||||||
@ -78,9 +73,7 @@ body > a[style="cursor: pointer; float: right;"]::after {
|
|||||||
}
|
}
|
||||||
#boardNavDesktopFoot:hover::after,
|
#boardNavDesktopFoot:hover::after,
|
||||||
#globalMessage:hover::after,
|
#globalMessage:hover::after,
|
||||||
#imgControls .expand-all-shortcut:hover,
|
#img-controls:hover,
|
||||||
#imgControls .contract-all-shortcut:hover,
|
|
||||||
#imgControls .menu-button:hover,
|
|
||||||
#navlinks a:hover,
|
#navlinks a:hover,
|
||||||
#appchanOptions:hover,
|
#appchanOptions:hover,
|
||||||
#navtopright .exlinksOptionsLink:hover::after,
|
#navtopright .exlinksOptionsLink:hover::after,
|
||||||
|
|||||||
@ -24,12 +24,7 @@ body > a[style="cursor: pointer; float: right;"]::after {
|
|||||||
#{align}: #{position[i++]}px;
|
#{align}: #{position[i++]}px;
|
||||||
}
|
}
|
||||||
/* Expand Images */
|
/* Expand Images */
|
||||||
#imgControls .expand-all-shortcut,
|
#img-controls {
|
||||||
#imgControls .contract-all-shortcut {
|
|
||||||
#{align}: #{position[i++]}px;
|
|
||||||
}
|
|
||||||
/* Expand Images Menu */
|
|
||||||
#imgControls .menu-button {
|
|
||||||
#{align}: #{position[i++]}px;
|
#{align}: #{position[i++]}px;
|
||||||
}
|
}
|
||||||
/* 4chan Catalog */
|
/* 4chan Catalog */
|
||||||
@ -66,9 +61,7 @@ div.navLinks > a:first-of-type::after {
|
|||||||
#appchanOptions,
|
#appchanOptions,
|
||||||
#watcher::after,
|
#watcher::after,
|
||||||
#globalMessage::after,
|
#globalMessage::after,
|
||||||
#imgControls .expand-all-shortcut,
|
#img-controls,
|
||||||
#imgControls .contract-all-shortcut,
|
|
||||||
#imgControls .menu-button,
|
|
||||||
#fappeTyme,
|
#fappeTyme,
|
||||||
div.navLinks > a:first-of-type::after,
|
div.navLinks > a:first-of-type::after,
|
||||||
#catalog::after,
|
#catalog::after,
|
||||||
|
|||||||
@ -1,11 +1,6 @@
|
|||||||
|
|
||||||
/* Expand Images */
|
/* Expand Images */
|
||||||
#imgControls .expand-all-shortcut,
|
#img-controls {
|
||||||
#imgControls .contract-all-shortcut {
|
|
||||||
top: #{position[i++]}px;
|
|
||||||
}
|
|
||||||
/* Expand Images Menu */
|
|
||||||
#imgControls .menu-button {
|
|
||||||
top: #{position[i++]}px;
|
top: #{position[i++]}px;
|
||||||
}
|
}
|
||||||
/* 4chan X Options */
|
/* 4chan X Options */
|
||||||
@ -64,9 +59,7 @@ div.navLinks > a:first-of-type::after {
|
|||||||
#appchanOptions,
|
#appchanOptions,
|
||||||
#boardNavDesktopFoot::after,
|
#boardNavDesktopFoot::after,
|
||||||
#globalMessage::after,
|
#globalMessage::after,
|
||||||
#imgControls .expand-all-shortcut,
|
#img-controls,
|
||||||
#imgControls .contract-all-shortcut,
|
|
||||||
#imgControls .menu-button,
|
|
||||||
#fappeTyme,
|
#fappeTyme,
|
||||||
#{if _conf["Slideout Watcher"] then "#watcher::after," else ""}
|
#{if _conf["Slideout Watcher"] then "#watcher::after," else ""}
|
||||||
body > a[style="cursor: pointer; float: right;"]::after,
|
body > a[style="cursor: pointer; float: right;"]::after,
|
||||||
|
|||||||
131
css/layout.css
131
css/layout.css
@ -118,7 +118,7 @@ hr {
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
/* Symbols */
|
/* Symbols */
|
||||||
.dropmarker {
|
.drop-marker {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 2px 2px 3px;
|
margin: 2px 2px 3px;
|
||||||
@ -126,6 +126,12 @@ hr {
|
|||||||
border-right: .3em solid transparent;
|
border-right: .3em solid transparent;
|
||||||
border-left: .3em solid transparent;
|
border-left: .3em solid transparent;
|
||||||
}
|
}
|
||||||
|
.brackets-wrap::before {
|
||||||
|
content: "\\00a0[";
|
||||||
|
}
|
||||||
|
.brackets-wrap::after {
|
||||||
|
content: "]\\00a0";
|
||||||
|
}
|
||||||
/* Thread / Reply Nav */
|
/* Thread / Reply Nav */
|
||||||
#navlinks a {
|
#navlinks a {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -140,30 +146,7 @@ hr {
|
|||||||
#boardNavDesktop {
|
#boardNavDesktop {
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
#{{
|
|
||||||
"sticky top": "
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
border-top-width: 0;
|
|
||||||
#{if _conf["Rounded Edges"] then "border-radius: 0 0 3px 3px;" else ""}"
|
|
||||||
|
|
||||||
"sticky bottom": "
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
border-bottom-width: 0;
|
|
||||||
#{if _conf["Rounded Edges"] then "border-radius: 3px 3px 0 0;" else ""}"
|
|
||||||
|
|
||||||
"top": "
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
|
||||||
border-top-width: 0;
|
|
||||||
#{if _conf["Rounded Edges"] then "border-radius: 0 0 3px 3px;" else ""}"
|
|
||||||
|
|
||||||
"hide": "
|
|
||||||
position: fixed;
|
|
||||||
top: 110%;"
|
|
||||||
|
|
||||||
}[_conf['Boards Navigation']]}
|
|
||||||
#{
|
#{
|
||||||
if _conf['4chan SS Navigation'] then "
|
if _conf['4chan SS Navigation'] then "
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -182,6 +165,71 @@ else "
|
|||||||
" else ""}
|
" else ""}
|
||||||
text-align: #{_conf["Navigation Alignment"]};
|
text-align: #{_conf["Navigation Alignment"]};
|
||||||
}
|
}
|
||||||
|
.fixed #boardNavDesktop {
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
.top #boardNavDesktop {
|
||||||
|
top: 0;
|
||||||
|
border-top-width: 0;
|
||||||
|
#{if _conf["Rounded Edges"] then "border-radius: 0 0 3px 3px;" else ""}"
|
||||||
|
}
|
||||||
|
.fixed.bottom #boardNavDesktop {
|
||||||
|
bottom: 0;
|
||||||
|
border-bottom-width: 0;
|
||||||
|
#{if _conf["Rounded Edges"] then "border-radius: 3px 3px 0 0;" else ""}"
|
||||||
|
}
|
||||||
|
.hide #boardNavDesktop {
|
||||||
|
position: fixed;
|
||||||
|
top: 110%;
|
||||||
|
bottom: auto;
|
||||||
|
}
|
||||||
|
/* Header Autohide */
|
||||||
|
.fixed #boardNavDesktop.autohide:not(:hover) {
|
||||||
|
box-shadow: none;
|
||||||
|
transition: all .8s .6s cubic-bezier(.55, .055, .675, .19);
|
||||||
|
}
|
||||||
|
.fixed.top #boardNavDesktop.autohide:not(:hover) {
|
||||||
|
margin-bottom: -1em;
|
||||||
|
-webkit-transform: translateY(-100%);
|
||||||
|
transform: translateY(-100%);
|
||||||
|
}
|
||||||
|
.fixed.bottom #boardNavDesktop.autohide:not(:hover) {
|
||||||
|
-webkit-transform: translateY(100%);
|
||||||
|
transform: translateY(100%);
|
||||||
|
}
|
||||||
|
#toggle-header-bar {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 10px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
#boardNavDesktop #toggle-header-bar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.fixed #boardNavDesktop #toggle-header-bar {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.fixed #boardNavDesktop #toggle-header-bar {
|
||||||
|
cursor: n-resize;
|
||||||
|
}
|
||||||
|
.fixed.top boardNavDesktop #toggle-header-bar {
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
|
.fixed.bottom #boardNavDesktop #toggle-header-bar {
|
||||||
|
bottom: 100%;
|
||||||
|
}
|
||||||
|
.fixed #boardNavDesktop #header-bar.autohide #toggle-header-bar {
|
||||||
|
cursor: s-resize;
|
||||||
|
}
|
||||||
|
/* Notifications */
|
||||||
|
.notification {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
.close {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
/* Pagination */
|
/* Pagination */
|
||||||
.pagelist {
|
.pagelist {
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
@ -443,27 +491,6 @@ else "
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
/* Image Expansion */
|
/* Image Expansion */
|
||||||
#imgControls a.menu-button {
|
|
||||||
margin: 0;
|
|
||||||
margin: 1px;
|
|
||||||
border: 2px solid;
|
|
||||||
border-radius: 10px;
|
|
||||||
height: 14px;
|
|
||||||
width: 14px;
|
|
||||||
#{Style.sizing}: border-box;
|
|
||||||
}
|
|
||||||
#imgControls .menu-button::after {
|
|
||||||
content: '';
|
|
||||||
font-size: 10px;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 50%;
|
|
||||||
right: 50%;
|
|
||||||
#{agent}transform: translate(50%, 60%);
|
|
||||||
display: block;
|
|
||||||
border-top: 6px solid rgb(130, 130, 130);
|
|
||||||
border-left: 3px solid transparent;
|
|
||||||
border-right: 3px solid transparent;
|
|
||||||
}
|
|
||||||
.fit-width .full-image {
|
.fit-width .full-image {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -691,7 +718,7 @@ hide: "
|
|||||||
.menu-button {
|
.menu-button {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.menu-button,
|
.post .menu-button,
|
||||||
.hide-thread-button,
|
.hide-thread-button,
|
||||||
.hide-reply-button {
|
.hide-reply-button {
|
||||||
float: right;
|
float: right;
|
||||||
@ -1037,7 +1064,7 @@ input:checked + .rice {
|
|||||||
#{agent}transform: translateX(#{xOffset}93%);
|
#{agent}transform: translateX(#{xOffset}93%);
|
||||||
}
|
}
|
||||||
#qr:hover,
|
#qr:hover,
|
||||||
#qr.focus,
|
#qr.has-focus,
|
||||||
#qr.dump {
|
#qr.dump {
|
||||||
#{agent}transform: translate(0);
|
#{agent}transform: translate(0);
|
||||||
}"
|
}"
|
||||||
@ -1051,7 +1078,7 @@ input:checked + .rice {
|
|||||||
#{agent}transform: translateX(#{xOffset}100%);
|
#{agent}transform: translateX(#{xOffset}100%);
|
||||||
}
|
}
|
||||||
#qr:hover,
|
#qr:hover,
|
||||||
#qr.focus,
|
#qr.has-focus,
|
||||||
#qr.dump {
|
#qr.dump {
|
||||||
#{agent}transform: translateX(0);
|
#{agent}transform: translateX(0);
|
||||||
}
|
}
|
||||||
@ -1067,7 +1094,7 @@ input:checked + .rice {
|
|||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
#qr:hover #qrtab,
|
#qr:hover #qrtab,
|
||||||
#qr.focus #qrtab,
|
#qr.has-focus #qrtab,
|
||||||
#qr.dump #qrtab {
|
#qr.dump #qrtab {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
#{Style.sidebarLocation[0]}: #{252 + Style.sidebarOffset.W}px;
|
#{Style.sidebarLocation[0]}: #{252 + Style.sidebarOffset.W}px;
|
||||||
@ -1090,7 +1117,7 @@ input:checked + .rice {
|
|||||||
#{agent}transition: opacity .3s ease-in-out 1s;
|
#{agent}transition: opacity .3s ease-in-out 1s;
|
||||||
}
|
}
|
||||||
#qr:hover,
|
#qr:hover,
|
||||||
#qr.focus,
|
#qr.has-focus,
|
||||||
#qr.dump {
|
#qr.dump {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
#{agent}transition: opacity .3s linear;
|
#{agent}transition: opacity .3s linear;
|
||||||
@ -1103,7 +1130,7 @@ unless _conf['Post Form Style'] is 'tabbed slideout'
|
|||||||
unless _conf['Post Form Style'] is 'float' or _conf['Show Post Form Header']
|
unless _conf['Post Form Style'] is 'float' or _conf['Show Post Form Header']
|
||||||
"#qrtab { display: none; }"
|
"#qrtab { display: none; }"
|
||||||
else unless _conf['Post Form Style'] is 'slideout'
|
else unless _conf['Post Form Style'] is 'slideout'
|
||||||
".autohide:not(:hover):not(.focus) > form { display: none !important; }"
|
".autohide:not(:hover):not(.has-focus) > form { display: none !important; }"
|
||||||
else ""
|
else ""
|
||||||
) + "#qrtab { margin-bottom: 1px; }"
|
) + "#qrtab { margin-bottom: 1px; }"
|
||||||
else ""}
|
else ""}
|
||||||
@ -1114,7 +1141,7 @@ if _conf['Post Form Style'] isnt 'float' and _conf["Post Form Slideout Transitio
|
|||||||
#{agent}transition: #{agent}transform .3s ease-in-out 1s;
|
#{agent}transition: #{agent}transform .3s ease-in-out 1s;
|
||||||
}
|
}
|
||||||
#qr:hover,
|
#qr:hover,
|
||||||
#qr.focus,
|
#qr.has-focus,
|
||||||
#qr.dump {
|
#qr.dump {
|
||||||
#{agent}transition: #{agent}transform .3s linear;
|
#{agent}transition: #{agent}transform .3s linear;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -211,10 +211,6 @@ a[style="cursor: pointer; float: right;"] ~ div[style^="width: 100%;"] > table {
|
|||||||
#navlinks a:last-of-type {
|
#navlinks a:last-of-type {
|
||||||
border-top: 11px solid rgb(130,130,130);
|
border-top: 11px solid rgb(130,130,130);
|
||||||
}
|
}
|
||||||
#imgControls .menu-button {
|
|
||||||
border-color: rgb(130,130,130);
|
|
||||||
color: rgb(130,130,130);
|
|
||||||
}
|
|
||||||
#charCount {
|
#charCount {
|
||||||
color: #{(if Style.lightTheme then "rgba(0,0,0,0.7)" else "rgba(255,255,255,0.7)")};
|
color: #{(if Style.lightTheme then "rgba(0,0,0,0.7)" else "rgba(255,255,255,0.7)")};
|
||||||
}
|
}
|
||||||
@ -396,8 +392,7 @@ a .name {
|
|||||||
#globalMessage::after,
|
#globalMessage::after,
|
||||||
#boardNavDesktopFoot::after,
|
#boardNavDesktopFoot::after,
|
||||||
a[style="cursor: pointer; float: right;"]::after,
|
a[style="cursor: pointer; float: right;"]::after,
|
||||||
#imgControls .expand-all-shortcut,
|
#img-controls,
|
||||||
#imgControls .contract-all-shortcut,
|
|
||||||
#catalog::after,
|
#catalog::after,
|
||||||
#fappeTyme {
|
#fappeTyme {
|
||||||
background-image: url('#{icons}');
|
background-image: url('#{icons}');
|
||||||
|
|||||||
12
lib/$.coffee
12
lib/$.coffee
@ -140,8 +140,16 @@ $.extend $,
|
|||||||
el.classList.toggle className
|
el.classList.toggle className
|
||||||
hasClass: (el, className) ->
|
hasClass: (el, className) ->
|
||||||
el.classList.contains className
|
el.classList.contains className
|
||||||
rm: (el) ->
|
rm: do ->
|
||||||
el.parentNode.removeChild el
|
if 'remove' of Element.prototype
|
||||||
|
(el) -> el.remove()
|
||||||
|
else
|
||||||
|
(el) -> el.parentNode?.removeChild el
|
||||||
|
rmAll: (root) ->
|
||||||
|
# jsperf.com/emptify-element
|
||||||
|
while node = root.firstChild
|
||||||
|
$.rm node
|
||||||
|
return
|
||||||
tn: (s) ->
|
tn: (s) ->
|
||||||
d.createTextNode s
|
d.createTextNode s
|
||||||
frag: ->
|
frag: ->
|
||||||
|
|||||||
@ -4,7 +4,8 @@ UI = do ->
|
|||||||
className: 'dialog'
|
className: 'dialog'
|
||||||
innerHTML: html
|
innerHTML: html
|
||||||
id: id
|
id: id
|
||||||
el.style.cssText = localStorage.getItem("#{g.NAMESPACE}#{id}.position") or position
|
$.get "#{id}.position", position, (item) ->
|
||||||
|
el.style.cssText = item["#{id}.position"]
|
||||||
move = $ '.move', el
|
move = $ '.move', el
|
||||||
$.on move, 'touchstart mousedown', dragstart
|
$.on move, 'touchstart mousedown', dragstart
|
||||||
for child in move.children
|
for child in move.children
|
||||||
@ -280,7 +281,7 @@ UI = do ->
|
|||||||
else # mouseup
|
else # mouseup
|
||||||
$.off d, 'mousemove', @move
|
$.off d, 'mousemove', @move
|
||||||
$.off d, 'mouseup', @up
|
$.off d, 'mouseup', @up
|
||||||
localStorage.setItem "#{g.NAMESPACE}#{@id}.position", @style.cssText
|
$.set "#{@id}.position", @style.cssText
|
||||||
|
|
||||||
hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb, close}) ->
|
hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb, close}) ->
|
||||||
o = {
|
o = {
|
||||||
|
|||||||
@ -240,7 +240,6 @@ Style =
|
|||||||
$ '#navtopright .exlinksOptionsLink', d.body
|
$ '#navtopright .exlinksOptionsLink', d.body
|
||||||
notCatalog and $ 'body > a[style="cursor: pointer; float: right;"]', d.body
|
notCatalog and $ 'body > a[style="cursor: pointer; float: right;"]', d.body
|
||||||
notEither and _conf['Image Expansion']
|
notEither and _conf['Image Expansion']
|
||||||
notEither and _conf['Image Expansion']
|
|
||||||
notEither
|
notEither
|
||||||
g.VIEW is 'thread'
|
g.VIEW is 'thread'
|
||||||
notEither and _conf['Fappe Tyme']
|
notEither and _conf['Fappe Tyme']
|
||||||
@ -265,7 +264,6 @@ Style =
|
|||||||
position = aligner(
|
position = aligner(
|
||||||
2 + (if _conf["4chan Banner"] is "at sidebar top" then (Style.logoOffset + 19) else 0)
|
2 + (if _conf["4chan Banner"] is "at sidebar top" then (Style.logoOffset + 19) else 0)
|
||||||
[
|
[
|
||||||
notEither and _conf['Image Expansion']
|
|
||||||
notEither and _conf['Image Expansion']
|
notEither and _conf['Image Expansion']
|
||||||
notCatalog
|
notCatalog
|
||||||
_conf['Slideout Navigation'] isnt 'hide'
|
_conf['Slideout Navigation'] isnt 'hide'
|
||||||
@ -314,12 +312,11 @@ Style =
|
|||||||
Style.padding.pages.property = _conf["Pagination"].split(" ")
|
Style.padding.pages.property = _conf["Pagination"].split(" ")
|
||||||
Style.padding.pages.property = Style.padding.pages.property[Style.padding.pages.property.length - 1]
|
Style.padding.pages.property = Style.padding.pages.property[Style.padding.pages.property.length - 1]
|
||||||
css = "body::before {\n"
|
css = "body::before {\n"
|
||||||
if _conf["4chan SS Emulation"]
|
if Style.padding.pages and (_conf["Pagination"] is "sticky top" or _conf["Pagination"] is "sticky bottom")
|
||||||
if Style.padding.pages and (_conf["Pagination"] is "sticky top" or _conf["Pagination"] is "sticky bottom")
|
css += " #{Style.padding.pages.property}: #{Style.padding.pages.offsetHeight}px !important;\n"
|
||||||
css += " #{Style.padding.pages.property}: #{Style.padding.pages.offsetHeight}px !important;\n"
|
|
||||||
|
|
||||||
if _conf["Boards Navigation"] is "sticky top" or _conf["Boards Navigation"] is "sticky bottom"
|
if _conf["Boards Navigation"] is "sticky top" or _conf["Boards Navigation"] is "sticky bottom"
|
||||||
css += " #{Style.padding.nav.property}: #{Style.padding.nav.offsetHeight}px !important;\n"
|
css += " #{Style.padding.nav.property}: #{Style.padding.nav.offsetHeight}px !important;\n"
|
||||||
|
|
||||||
css += """
|
css += """
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,10 @@ Config =
|
|||||||
false
|
false
|
||||||
'Compatibility between <%= meta.name %> and 4chan\'s inline extension is NOT guaranteed.'
|
'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.'
|
||||||
@ -225,6 +229,12 @@ Config =
|
|||||||
false
|
false
|
||||||
'Remember the subject field, instead of resetting after posting.'
|
'Remember the subject field, instead of resetting after posting.'
|
||||||
]
|
]
|
||||||
|
<% if (type === 'userscript') { %>
|
||||||
|
'Remember QR Size': [
|
||||||
|
false
|
||||||
|
'Remember the size of the Quick reply.'
|
||||||
|
]
|
||||||
|
<% } %>
|
||||||
'Remember Spoiler': [
|
'Remember Spoiler': [
|
||||||
false
|
false
|
||||||
'Remember the spoiler state, instead of resetting after posting.'
|
'Remember the spoiler state, instead of resetting after posting.'
|
||||||
@ -586,11 +596,6 @@ Config =
|
|||||||
]
|
]
|
||||||
|
|
||||||
Navigation:
|
Navigation:
|
||||||
'Boards Navigation': [
|
|
||||||
'sticky top'
|
|
||||||
'The position of 4chan board navigation'
|
|
||||||
['sticky top', 'sticky bottom', 'top', 'hide']
|
|
||||||
]
|
|
||||||
'Navigation Alignment': [
|
'Navigation Alignment': [
|
||||||
'center'
|
'center'
|
||||||
'Change the text alignment of the navigation.'
|
'Change the text alignment of the navigation.'
|
||||||
@ -716,7 +721,11 @@ http://iqdb.org/?url=%TURL
|
|||||||
#//archive.installgentoo.net/%board/image/%MD5;text:View same on installgentoo /%board/
|
#//archive.installgentoo.net/%board/image/%MD5;text:View same on installgentoo /%board/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
'Custom CSS': false
|
'Boards Navigation': 'sticky top'
|
||||||
|
|
||||||
|
'Custom CSS': false
|
||||||
|
|
||||||
|
'Bottom header': false
|
||||||
|
|
||||||
'Header auto-hide': false
|
'Header auto-hide': false
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,37 @@
|
|||||||
Header =
|
Header =
|
||||||
init: ->
|
init: ->
|
||||||
@bar = $.el 'div',
|
@menuButton = $.el 'span',
|
||||||
id: 'notifications'
|
className: 'menu-button'
|
||||||
@shortcuts = $.el 'span',
|
innerHTML: '<a class=brackets-wrap href=javascript:;><i class=drop-marker></i></a>'
|
||||||
id: 'shortcuts'
|
|
||||||
@hover = $.el 'div',
|
|
||||||
id: 'hoverUI'
|
|
||||||
|
|
||||||
$.on window, 'load hashchange', Header.hashScroll
|
@menu = new UI.Menu 'header'
|
||||||
|
$.on @menuButton, 'click', @menuToggle
|
||||||
|
$.on @toggle, 'mousedown', @toggleBarVisibility
|
||||||
|
$.on window, 'load hashchange', Header.hashScroll
|
||||||
|
|
||||||
|
@positionToggler = $.el 'span',
|
||||||
|
textContent: 'Header Position'
|
||||||
|
className: 'header-position-link'
|
||||||
|
|
||||||
|
{createSubEntry} = Header
|
||||||
|
subEntries = []
|
||||||
|
for setting in ['sticky top', 'sticky bottom', 'top', 'hide']
|
||||||
|
subEntries.push createSubEntry setting
|
||||||
|
|
||||||
|
$.event 'AddMenuEntry',
|
||||||
|
type: 'header'
|
||||||
|
el: @positionToggler
|
||||||
|
order: 108
|
||||||
|
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
|
||||||
|
|
||||||
@ -20,18 +44,48 @@ Header =
|
|||||||
$.ready ->
|
$.ready ->
|
||||||
$.add d.body, Header.hover
|
$.add d.body, Header.hover
|
||||||
|
|
||||||
setBoardList: ->
|
bar: $.el 'div',
|
||||||
|
id: 'notifications'
|
||||||
|
|
||||||
|
shortcuts: $.el 'span',
|
||||||
|
id: 'shortcuts'
|
||||||
|
|
||||||
|
hover: $.el 'div',
|
||||||
|
id: 'hoverUI'
|
||||||
|
|
||||||
|
toggle: $.el 'div',
|
||||||
|
id: 'toggle-header-bar'
|
||||||
|
|
||||||
|
createSubEntry: (setting)->
|
||||||
|
label = $.el 'label',
|
||||||
|
textContent: "#{setting}"
|
||||||
|
|
||||||
|
$.on label, 'click', Header.setBarPosition
|
||||||
|
|
||||||
|
el: label
|
||||||
|
|
||||||
|
setBoardList: ->
|
||||||
Header.nav = nav = $.id 'boardNavDesktop'
|
Header.nav = nav = $.id 'boardNavDesktop'
|
||||||
if a = $ "a[href*='/#{g.BOARD}/']", nav
|
if a = $ "a[href*='/#{g.BOARD}/']", nav
|
||||||
a.className = 'current'
|
a.className = 'current'
|
||||||
|
|
||||||
fullBoardList = $.el 'span',
|
fullBoardList = $.el 'span',
|
||||||
id: 'full-board-list'
|
id: 'full-board-list'
|
||||||
hidden: true
|
hidden: true
|
||||||
|
|
||||||
customBoardList = $.el 'span',
|
customBoardList = $.el 'span',
|
||||||
id: 'custom-board-list'
|
id: 'custom-board-list'
|
||||||
|
|
||||||
|
Header.setBarPosition.call textContent: "#{Conf['Boards Navigation']}"
|
||||||
|
$.sync 'Boards Navigation', ->
|
||||||
|
Header.setBarPosition.call textContent: "#{Conf['Boards Navigation']}"
|
||||||
|
|
||||||
|
Header.setBarVisibility Conf['Header auto-hide']
|
||||||
|
$.sync 'Header auto-hide', Header.setBarVisibility
|
||||||
|
|
||||||
$.add fullBoardList, [nav.childNodes...]
|
$.add fullBoardList, [nav.childNodes...]
|
||||||
$.add nav, [customBoardList, fullBoardList, Header.shortcuts, $ '#navtopright', fullBoardList, Header.bar]
|
$.add nav, [Header.menuButton, customBoardList, fullBoardList, Header.shortcuts, $('#navtopright', fullBoardList), Header.toggle]
|
||||||
|
$.add d.body, Header.bar
|
||||||
|
|
||||||
if Conf['Custom Board Navigation']
|
if Conf['Custom Board Navigation']
|
||||||
Header.generateBoardList Conf['boardnav']
|
Header.generateBoardList Conf['boardnav']
|
||||||
@ -47,7 +101,7 @@ Header =
|
|||||||
|
|
||||||
generateBoardList: (text) ->
|
generateBoardList: (text) ->
|
||||||
list = $ '#custom-board-list', Header.nav
|
list = $ '#custom-board-list', Header.nav
|
||||||
list.innerHTML = null
|
$.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|full|index|catalog|text:"[^"]+"))*|[^\w@]+/g).map (t) ->
|
||||||
@ -92,8 +146,41 @@ Header =
|
|||||||
custom.hidden = !showBoardList
|
custom.hidden = !showBoardList
|
||||||
full.hidden = showBoardList
|
full.hidden = showBoardList
|
||||||
|
|
||||||
|
setBarPosition: ->
|
||||||
|
$.event 'CloseMenu'
|
||||||
|
|
||||||
|
switch @textContent
|
||||||
|
when 'sticky top'
|
||||||
|
$.addClass doc, 'top'
|
||||||
|
$.addClass doc, 'fixed'
|
||||||
|
$.rmClass doc, 'bottom'
|
||||||
|
$.rmClass doc, 'hide'
|
||||||
|
when 'sticky bottom'
|
||||||
|
$.rmClass doc, 'top'
|
||||||
|
$.addClass doc, 'fixed'
|
||||||
|
$.addClass doc, 'bottom'
|
||||||
|
$.rmClass doc, 'hide'
|
||||||
|
when 'top'
|
||||||
|
$.addClass doc, 'top'
|
||||||
|
$.rmClass doc, 'fixed'
|
||||||
|
$.rmClass doc, 'bottom'
|
||||||
|
$.rmClass doc, 'hide'
|
||||||
|
when 'hide'
|
||||||
|
$.rmClass doc, 'top'
|
||||||
|
$.rmClass doc, 'fixed'
|
||||||
|
$.rmClass doc, 'bottom'
|
||||||
|
$.addClass doc, 'hide'
|
||||||
|
|
||||||
|
Conf['Boards Navigation'] = @textContent
|
||||||
|
$.set 'Boards Navigation', @textContent
|
||||||
|
|
||||||
|
setBarVisibility: (hide) ->
|
||||||
|
Header.headerToggler.firstElementChild.checked = hide
|
||||||
|
(if hide then $.addClass else $.rmClass) Header.nav, 'autohide'
|
||||||
|
|
||||||
hashScroll: ->
|
hashScroll: ->
|
||||||
return unless post = $.id @location.hash[1..]
|
return unless post = $.id @location.hash[1..]
|
||||||
|
return if (Get.postFromRoot post).isHidden
|
||||||
Header.scrollToPost post
|
Header.scrollToPost post
|
||||||
|
|
||||||
scrollToPost: (post) ->
|
scrollToPost: (post) ->
|
||||||
@ -103,12 +190,29 @@ Header =
|
|||||||
top += - headRect.top - headRect.height
|
top += - headRect.top - headRect.height
|
||||||
(if $.engine is 'webkit' then d.body else doc).scrollTop += top
|
(if $.engine is 'webkit' then d.body else doc).scrollTop += top
|
||||||
|
|
||||||
|
toggleBarVisibility: (e) ->
|
||||||
|
return if e.type is 'mousedown' and e.button isnt 0 # not LMB
|
||||||
|
hide = if @nodeName is 'INPUT'
|
||||||
|
@checked
|
||||||
|
else
|
||||||
|
!$.hasClass Header.nav, 'autohide'
|
||||||
|
Header.setBarVisibility hide
|
||||||
|
message = if hide
|
||||||
|
'The header bar will automatically hide itself.'
|
||||||
|
else
|
||||||
|
'The header bar will remain visible.'
|
||||||
|
new Notification 'info', message, 2
|
||||||
|
$.set 'Header auto-hide', hide
|
||||||
|
|
||||||
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
|
$.add Header.shortcuts, shortcut
|
||||||
|
|
||||||
|
menuToggle: (e) ->
|
||||||
|
Header.menu.toggle e, @, g
|
||||||
|
|
||||||
createNotification: (e) ->
|
createNotification: (e) ->
|
||||||
{type, content, lifetime, cb} = e.detail
|
{type, content, lifetime, cb} = e.detail
|
||||||
notif = new Notification type, content, lifetime
|
notif = new Notification type, content, lifetime
|
||||||
@ -144,7 +248,7 @@ class Notification
|
|||||||
setTimeout @close, @timeout * $.SECOND if @timeout
|
setTimeout @close, @timeout * $.SECOND if @timeout
|
||||||
|
|
||||||
close = ->
|
close = ->
|
||||||
$.rm @el if @el.parentNode
|
$.rm @el
|
||||||
|
|
||||||
CatalogLinks =
|
CatalogLinks =
|
||||||
init: ->
|
init: ->
|
||||||
@ -193,7 +297,6 @@ CatalogLinks =
|
|||||||
else
|
else
|
||||||
"//boards.4chan.org/#{board}/catalog"
|
"//boards.4chan.org/#{board}/catalog"
|
||||||
)
|
)
|
||||||
|
|
||||||
ready: ->
|
ready: ->
|
||||||
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'
|
||||||
@ -967,7 +1070,7 @@ Menu =
|
|||||||
(post) ->
|
(post) ->
|
||||||
a or= $.el 'a',
|
a or= $.el 'a',
|
||||||
className: 'menu-button'
|
className: 'menu-button'
|
||||||
innerHTML: '[<span class=dropmarker></span>]'
|
innerHTML: '[<span class=drop-marker></span>]'
|
||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
clone = a.cloneNode true
|
clone = a.cloneNode true
|
||||||
clone.setAttribute 'data-postid', post.fullID
|
clone.setAttribute 'data-postid', post.fullID
|
||||||
@ -1008,7 +1111,7 @@ ReportLink =
|
|||||||
|
|
||||||
DeleteLink =
|
DeleteLink =
|
||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW is 'catalog' or !Conf['Menu'] or !Conf['Delete Link'] or !Conf['Quick Reply']
|
return if g.VIEW is 'catalog' or !Conf['Menu'] or !Conf['Delete Link']
|
||||||
|
|
||||||
div = $.el 'div',
|
div = $.el 'div',
|
||||||
className: 'delete-link'
|
className: 'delete-link'
|
||||||
@ -1060,20 +1163,22 @@ DeleteLink =
|
|||||||
else
|
else
|
||||||
$.id('delPassword').value
|
$.id('delPassword').value
|
||||||
|
|
||||||
|
fileOnly = $.hasClass @, 'delete-file'
|
||||||
|
|
||||||
form =
|
form =
|
||||||
mode: 'usrdel'
|
mode: 'usrdel'
|
||||||
onlyimgdel: $.hasClass @, 'delete-file'
|
onlyimgdel: fileOnly
|
||||||
pwd: pwd
|
pwd: pwd
|
||||||
form[post.ID] = 'delete'
|
form[post.ID] = 'delete'
|
||||||
|
|
||||||
link = @
|
link = @
|
||||||
$.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{post.board}/"),
|
$.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{post.board}/"),
|
||||||
onload: -> DeleteLink.load link, post, @response
|
onload: -> DeleteLink.load link, post, fileOnly, @response
|
||||||
onerror: -> DeleteLink.error link
|
onerror: -> DeleteLink.error link
|
||||||
,
|
,
|
||||||
cred: true
|
cred: true
|
||||||
form: $.formData form
|
form: $.formData form
|
||||||
load: (link, post, html) ->
|
load: (link, post, fileOnly, html) ->
|
||||||
tmpDoc = d.implementation.createHTMLDocument ''
|
tmpDoc = d.implementation.createHTMLDocument ''
|
||||||
tmpDoc.documentElement.innerHTML = html
|
tmpDoc.documentElement.innerHTML = html
|
||||||
if tmpDoc.title is '4chan - Banned' # Ban/warn check
|
if tmpDoc.title is '4chan - Banned' # Ban/warn check
|
||||||
@ -1084,7 +1189,7 @@ DeleteLink =
|
|||||||
else
|
else
|
||||||
if tmpDoc.title is 'Updating index...'
|
if tmpDoc.title is 'Updating index...'
|
||||||
# We're 100% sure.
|
# We're 100% sure.
|
||||||
(post.origin or post).kill()
|
(post.origin or post).kill fileOnly
|
||||||
s = 'Deleted'
|
s = 'Deleted'
|
||||||
link.textContent = s
|
link.textContent = s
|
||||||
error: (link) ->
|
error: (link) ->
|
||||||
@ -1360,7 +1465,7 @@ Keybinds =
|
|||||||
location.href = url
|
location.href = url
|
||||||
|
|
||||||
hl: (delta, thread) ->
|
hl: (delta, thread) ->
|
||||||
if Conf['Bottom header']
|
if Conf['Fixed Header'] and Conf['Bottom header']
|
||||||
topMargin = 0
|
topMargin = 0
|
||||||
else
|
else
|
||||||
headRect = Header.bar.getBoundingClientRect()
|
headRect = Header.bar.getBoundingClientRect()
|
||||||
@ -1398,7 +1503,13 @@ Keybinds =
|
|||||||
|
|
||||||
Nav =
|
Nav =
|
||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW is 'index' and !Conf['Index Navigation'] or g.VIEW is 'thread' and !Conf['Reply Navigation']
|
switch g.VIEW
|
||||||
|
when 'index'
|
||||||
|
return unless Conf['Index Navigation']
|
||||||
|
when 'thread'
|
||||||
|
return unless Conf['Reply Navigation']
|
||||||
|
else # catalog
|
||||||
|
return
|
||||||
|
|
||||||
span = $.el 'span',
|
span = $.el 'span',
|
||||||
id: 'navlinks'
|
id: 'navlinks'
|
||||||
@ -1470,11 +1581,13 @@ Redirect =
|
|||||||
when 'c'
|
when 'c'
|
||||||
"//archive.nyafuu.org/#{boardID}/full_image/#{filename}"
|
"//archive.nyafuu.org/#{boardID}/full_image/#{filename}"
|
||||||
post: (boardID, postID) ->
|
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
|
switch boardID
|
||||||
when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg'
|
when 'a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg'
|
||||||
"//archive.foolz.us/_/api/chan/post/?board=#{boardID}&num=#{postID}"
|
"https://archive.foolz.us/_/api/chan/post/?board=#{boardID}&num=#{postID}"
|
||||||
when 'u'
|
when 'u'
|
||||||
"//nsfw.foolz.us/_/api/chan/post/?board=#{boardID}&num=#{postID}"
|
"https://nsfw.foolz.us/_/api/chan/post/?board=#{boardID}&num=#{postID}"
|
||||||
when 'c', 'int', 'out', 'po'
|
when 'c', 'int', 'out', 'po'
|
||||||
"//archive.thedarkcave.org/_/api/chan/post/?board=#{boardID}&num=#{postID}"
|
"//archive.thedarkcave.org/_/api/chan/post/?board=#{boardID}&num=#{postID}"
|
||||||
# for fuuka-based archives:
|
# for fuuka-based archives:
|
||||||
@ -1490,9 +1603,9 @@ Redirect =
|
|||||||
Redirect.path '//archive.thedarkcave.org', 'foolfuuka', data
|
Redirect.path '//archive.thedarkcave.org', 'foolfuuka', data
|
||||||
when 'ck', 'fa', 'lit', 's4s'
|
when 'ck', 'fa', 'lit', 's4s'
|
||||||
Redirect.path '//fuuka.warosu.org', 'fuuka', data
|
Redirect.path '//fuuka.warosu.org', 'fuuka', data
|
||||||
when 'diy', 'sci'
|
when 'diy', 'g', 'sci'
|
||||||
Redirect.path '//archive.installgentoo.net', 'fuuka', data
|
Redirect.path '//archive.installgentoo.net', 'fuuka', data
|
||||||
when 'cgl', 'g', 'mu', 'w'
|
when 'cgl', 'mu', 'w'
|
||||||
Redirect.path '//rbt.asia', 'fuuka', data
|
Redirect.path '//rbt.asia', 'fuuka', data
|
||||||
when 'an', 'fit', 'k', 'mlp', 'r9k', 'toy', 'x'
|
when 'an', 'fit', 'k', 'mlp', 'r9k', 'toy', 'x'
|
||||||
Redirect.path 'http://archive.heinessen.com', 'fuuka', data
|
Redirect.path 'http://archive.heinessen.com', 'fuuka', data
|
||||||
@ -1865,10 +1978,10 @@ Get =
|
|||||||
|
|
||||||
# Get rid of the side arrows.
|
# Get rid of the side arrows.
|
||||||
{nodes} = clone
|
{nodes} = clone
|
||||||
nodes.root.innerHTML = null
|
$.rmAll nodes.root
|
||||||
$.add nodes.root, nodes.post
|
$.add nodes.root, nodes.post
|
||||||
|
|
||||||
root.innerHTML = null
|
$.rmAll root
|
||||||
$.add root, nodes.root
|
$.add root, nodes.root
|
||||||
fetchedPost: (req, boardID, threadID, postID, root, context) ->
|
fetchedPost: (req, boardID, threadID, postID, root, context) ->
|
||||||
# In case of multiple callbacks for the same request,
|
# In case of multiple callbacks for the same request,
|
||||||
@ -2662,41 +2775,21 @@ ImageExpand =
|
|||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW is 'catalog' or !Conf['Image Expansion']
|
return if g.VIEW is 'catalog' or !Conf['Image Expansion']
|
||||||
|
|
||||||
wrapper = $.el 'div',
|
@EAI = $.el 'a',
|
||||||
id: 'imgControls'
|
id: 'img-controls'
|
||||||
innerHTML: """
|
className: 'expand-all-shortcut'
|
||||||
<a class='expand-all-shortcut' title='Expand All Images' href='javascript:;'></a>
|
title: 'Expand All Images'
|
||||||
<a class='menu-button' href='javascript:;'></a>
|
href: 'javascript:;'
|
||||||
"""
|
|
||||||
@EAI = wrapper.firstElementChild
|
|
||||||
$.on @EAI, 'click', ImageExpand.cb.toggleAll
|
$.on @EAI, 'click', ImageExpand.cb.toggleAll
|
||||||
|
|
||||||
@opmenu = new UI.Menu 'imageexpand'
|
|
||||||
$.on $('.menu-button', wrapper), 'click', @menuToggle
|
|
||||||
|
|
||||||
for type, config of Config.imageExpansion
|
|
||||||
label = $.el 'label',
|
|
||||||
innerHTML: "<input type=checkbox name='#{type}'> #{type}"
|
|
||||||
input = label.firstElementChild
|
|
||||||
if ['Fit width', 'Fit height'].contains type
|
|
||||||
$.on input, 'change', ImageExpand.cb.setFitness
|
|
||||||
|
|
||||||
if config
|
|
||||||
label.title = config[1]
|
|
||||||
input.checked = Conf[type]
|
|
||||||
$.event 'change', null, input
|
|
||||||
$.on input, 'change', $.cb.checked
|
|
||||||
|
|
||||||
$.event 'AddMenuEntry',
|
|
||||||
type: 'imageexpand'
|
|
||||||
el: label
|
|
||||||
|
|
||||||
$.asap (-> $.id 'delform'), ->
|
|
||||||
$.prepend $.id('delform'), wrapper
|
|
||||||
|
|
||||||
Post::callbacks.push
|
Post::callbacks.push
|
||||||
name: 'Image Expansion'
|
name: 'Image Expansion'
|
||||||
cb: @node
|
cb: @node
|
||||||
|
|
||||||
|
$.asap (-> $.id 'delform'), ->
|
||||||
|
$.prepend $.id('delform'), ImageExpand.EAI
|
||||||
|
|
||||||
node: ->
|
node: ->
|
||||||
return unless @file?.isImage
|
return unless @file?.isImage
|
||||||
{thumb} = @file
|
{thumb} = @file
|
||||||
@ -2843,7 +2936,7 @@ ImageExpand =
|
|||||||
|
|
||||||
el = $.el 'span',
|
el = $.el 'span',
|
||||||
textContent: 'Image Expansion'
|
textContent: 'Image Expansion'
|
||||||
className: 'image-expansion-link'
|
className: 'image-expansion-link'
|
||||||
|
|
||||||
{createSubEntry} = ImageExpand.menu
|
{createSubEntry} = ImageExpand.menu
|
||||||
subEntries = []
|
subEntries = []
|
||||||
@ -3190,18 +3283,22 @@ Unread =
|
|||||||
threadID: @ID
|
threadID: @ID
|
||||||
defaultValue: 0
|
defaultValue: 0
|
||||||
Unread.addPosts posts
|
Unread.addPosts posts
|
||||||
if (hash = location.hash.match /\d+/) and post = @posts[hash[0]]
|
|
||||||
Header.scrollToPost post.nodes.root
|
|
||||||
else if Unread.posts.length
|
|
||||||
# Scroll to before the first unread post.
|
|
||||||
$.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView false
|
|
||||||
else if posts.length
|
|
||||||
# Scroll to the last read post.
|
|
||||||
Header.scrollToPost posts[posts.length - 1].nodes.root
|
|
||||||
$.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']
|
||||||
|
|
||||||
|
return unless Conf['Scroll to Last Read Post']
|
||||||
|
# Let the header's onload callback handle it.
|
||||||
|
return if (hash = location.hash.match /\d+/) and hash[0] of @posts
|
||||||
|
if Unread.posts.length
|
||||||
|
# Scroll to before the first unread post.
|
||||||
|
while root = $.x 'preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root
|
||||||
|
break unless (Get.postFromRoot root).isHidden
|
||||||
|
root.scrollIntoView false
|
||||||
|
else if posts.length
|
||||||
|
# Scroll to the last read post.
|
||||||
|
Header.scrollToPost posts[posts.length - 1].nodes.root
|
||||||
|
|
||||||
sync: ->
|
sync: ->
|
||||||
lastReadPost = Unread.db.get
|
lastReadPost = Unread.db.get
|
||||||
boardID: Unread.thread.board.ID
|
boardID: Unread.thread.board.ID
|
||||||
@ -3287,7 +3384,7 @@ Unread =
|
|||||||
{root} = post.nodes
|
{root} = post.nodes
|
||||||
if root isnt $ '.thread > .replyContainer', root.parentNode # not the first reply
|
if root isnt $ '.thread > .replyContainer', root.parentNode # not the first reply
|
||||||
$.before root, Unread.hr
|
$.before root, Unread.hr
|
||||||
else if Unread.hr.parentNode
|
else
|
||||||
$.rm Unread.hr
|
$.rm Unread.hr
|
||||||
|
|
||||||
update: <% if (type === 'crx') { %>(dontrepeat) <% } %>->
|
update: <% if (type === 'crx') { %>(dontrepeat) <% } %>->
|
||||||
@ -3581,7 +3678,7 @@ ThreadUpdater =
|
|||||||
unless d.hidden
|
unless d.hidden
|
||||||
# Lower the max refresh rate limit on visible tabs.
|
# Lower the max refresh rate limit on visible tabs.
|
||||||
j = Math.min j, 7
|
j = Math.min j, 7
|
||||||
ThreadUpdater.seconds =
|
ThreadUpdater.seconds =
|
||||||
if Conf['Optional Increase']
|
if Conf['Optional Increase']
|
||||||
Math.max i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]
|
Math.max i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]
|
||||||
else
|
else
|
||||||
@ -3781,7 +3878,7 @@ ThreadWatcher =
|
|||||||
$.add div, [x, $.tn(' '), link]
|
$.add div, [x, $.tn(' '), link]
|
||||||
nodes.push div
|
nodes.push div
|
||||||
|
|
||||||
ThreadWatcher.dialog.innerHTML = ''
|
$.rmAll ThreadWatcher.dialog
|
||||||
$.add ThreadWatcher.dialog, nodes
|
$.add ThreadWatcher.dialog, nodes
|
||||||
|
|
||||||
watched = watched[g.BOARD] or {}
|
watched = watched[g.BOARD] or {}
|
||||||
@ -4035,7 +4132,7 @@ Linkify =
|
|||||||
|
|
||||||
embedder: (a) ->
|
embedder: (a) ->
|
||||||
return [a] unless Conf['Embedding']
|
return [a] unless Conf['Embedding']
|
||||||
|
|
||||||
callbacks = ->
|
callbacks = ->
|
||||||
a.textContent = switch @status
|
a.textContent = switch @status
|
||||||
when 200, 304
|
when 200, 304
|
||||||
@ -4066,16 +4163,17 @@ Linkify =
|
|||||||
$.on embed, 'click', Linkify.toggle
|
$.on embed, 'click', Linkify.toggle
|
||||||
|
|
||||||
if Conf['Link Title'] and (service = type.title)
|
if Conf['Link Title'] and (service = type.title)
|
||||||
titles = $.get 'CachedTitles', {}
|
$.get 'CachedTitles', {}, (item) ->
|
||||||
|
titles = item['CachedTitles']
|
||||||
|
|
||||||
if title = titles[match[1]]
|
if title = titles[match[1]]
|
||||||
a.textContent = title[0]
|
a.textContent = title[0]
|
||||||
embed.setAttribute 'data-title', title[0]
|
embed.setAttribute 'data-title', title[0]
|
||||||
else
|
else
|
||||||
try
|
try
|
||||||
$.cache service.api.call(a), callbacks
|
$.cache service.api.call(a), callbacks
|
||||||
catch err
|
catch err
|
||||||
a.innerHTML = "[#{key}] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>"
|
a.innerHTML = "[#{key}] <span class=warning>Title Link Blocked</span> (are you using NoScript?)</a>"
|
||||||
|
|
||||||
return [a, $.tn(' '), embed]
|
return [a, $.tn(' '), embed]
|
||||||
return [a]
|
return [a]
|
||||||
|
|||||||
@ -432,7 +432,7 @@ Main =
|
|||||||
|
|
||||||
# c.timeEnd 'All initializations'
|
# c.timeEnd 'All initializations'
|
||||||
|
|
||||||
$.on d, 'AddCallback', Main.addCallback
|
$.on d, 'AddCallback', Main.addCallback
|
||||||
$.ready Main.initReady
|
$.ready Main.initReady
|
||||||
|
|
||||||
initReady: ->
|
initReady: ->
|
||||||
|
|||||||
107
src/qr.coffee
107
src/qr.coffee
@ -11,9 +11,9 @@ QR =
|
|||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
$.on sc, 'click', ->
|
$.on sc, 'click', ->
|
||||||
if !QR.nodes or QR.nodes.el.hidden
|
if !QR.nodes or QR.nodes.el.hidden
|
||||||
|
$.event 'CloseMenu'
|
||||||
QR.open()
|
QR.open()
|
||||||
QR.nodes.com.focus()
|
QR.nodes.com.focus()
|
||||||
QR.resetThreadSelector()
|
|
||||||
else
|
else
|
||||||
QR.close()
|
QR.close()
|
||||||
$.toggleClass @, 'disabled'
|
$.toggleClass @, 'disabled'
|
||||||
@ -21,8 +21,7 @@ QR =
|
|||||||
Header.addShortcut sc
|
Header.addShortcut sc
|
||||||
|
|
||||||
if Conf['Hide Original Post Form']
|
if Conf['Hide Original Post Form']
|
||||||
$.asap (-> doc), ->
|
$.asap (-> doc), -> $.addClass doc, 'hide-original-post-form'
|
||||||
$.addClass doc, 'hide-original-post-form'
|
|
||||||
|
|
||||||
$.on d, '4chanXInitFinished', @initReady
|
$.on d, '4chanXInitFinished', @initReady
|
||||||
|
|
||||||
@ -81,6 +80,10 @@ QR =
|
|||||||
QR.status()
|
QR.status()
|
||||||
if !Conf['Remember Spoiler'] and QR.nodes.spoiler.checked
|
if !Conf['Remember Spoiler'] and QR.nodes.spoiler.checked
|
||||||
QR.nodes.spoiler.click()
|
QR.nodes.spoiler.click()
|
||||||
|
focusin: ->
|
||||||
|
$.addClass QR.nodes.el, 'has-focus'
|
||||||
|
focusout: ->
|
||||||
|
$.rmClass QR.nodes.el, 'has-focus'
|
||||||
hide: ->
|
hide: ->
|
||||||
d.activeElement.blur()
|
d.activeElement.blur()
|
||||||
$.addClass QR.nodes.el, 'autohide'
|
$.addClass QR.nodes.el, 'autohide'
|
||||||
@ -213,7 +216,7 @@ QR =
|
|||||||
|
|
||||||
now = Date.now()
|
now = Date.now()
|
||||||
post = QR.posts[0]
|
post = QR.posts[0]
|
||||||
isReply = QR.nodes.thread.value isnt 'new'
|
isReply = post.thread isnt 'new'
|
||||||
isSage = /sage/i.test post.email
|
isSage = /sage/i.test post.email
|
||||||
hasFile = !!post.file
|
hasFile = !!post.file
|
||||||
seconds = null
|
seconds = null
|
||||||
@ -272,19 +275,19 @@ QR =
|
|||||||
text += ">#{s}\n"
|
text += ">#{s}\n"
|
||||||
|
|
||||||
QR.open()
|
QR.open()
|
||||||
ta = QR.nodes.com
|
{com, thread} = QR.nodes
|
||||||
QR.nodes.thread.value = OP.ID unless ta.value
|
thread.value = OP.ID unless com.value
|
||||||
|
|
||||||
caretPos = ta.selectionStart
|
caretPos = com.selectionStart
|
||||||
# Replace selection for text.
|
# Replace selection for text.
|
||||||
ta.value = ta.value[...caretPos] + text + ta.value[ta.selectionEnd..]
|
com.value = com.value[...caretPos] + text + com.value[com.selectionEnd..]
|
||||||
# Move the caret to the end of the new quote.
|
# Move the caret to the end of the new quote.
|
||||||
range = caretPos + text.length
|
range = caretPos + text.length
|
||||||
ta.setSelectionRange range, range
|
com.setSelectionRange range, range
|
||||||
ta.focus()
|
com.focus()
|
||||||
|
|
||||||
# Fire the 'input' event
|
QR.selected.save com
|
||||||
$.event 'input', null, ta
|
QR.selected.save thread
|
||||||
|
|
||||||
characterCount: ->
|
characterCount: ->
|
||||||
counter = QR.nodes.charCount
|
counter = QR.nodes.charCount
|
||||||
@ -362,12 +365,6 @@ QR =
|
|||||||
post = new QR.post()
|
post = new QR.post()
|
||||||
post.setFile file
|
post.setFile file
|
||||||
$.addClass QR.nodes.el, 'dump'
|
$.addClass QR.nodes.el, 'dump'
|
||||||
|
|
||||||
resetThreadSelector: ->
|
|
||||||
if g.VIEW is 'thread'
|
|
||||||
QR.nodes.thread.value = g.THREADID
|
|
||||||
else
|
|
||||||
QR.nodes.thread.value = 'new'
|
|
||||||
|
|
||||||
posts: []
|
posts: []
|
||||||
|
|
||||||
@ -386,8 +383,12 @@ QR =
|
|||||||
spoiler: $ 'input', el
|
spoiler: $ 'input', el
|
||||||
span: el.lastChild
|
span: el.lastChild
|
||||||
|
|
||||||
@nodes.spoiler.checked = @spoiler
|
<% if (type === 'userscript') { %>
|
||||||
|
# XXX Firefox lacks focusin/focusout support.
|
||||||
|
for elm in $$ '*', el
|
||||||
|
$.on elm, 'blur', QR.focusout
|
||||||
|
$.on elm, 'focus', QR.focusin
|
||||||
|
<% } %>
|
||||||
$.on el, 'click', @select.bind @
|
$.on el, 'click', @select.bind @
|
||||||
$.on @nodes.rm, 'click', (e) => e.stopPropagation(); @rm()
|
$.on @nodes.rm, 'click', (e) => e.stopPropagation(); @rm()
|
||||||
$.on @nodes.label, 'click', (e) => e.stopPropagation()
|
$.on @nodes.label, 'click', (e) => e.stopPropagation()
|
||||||
@ -399,9 +400,14 @@ QR =
|
|||||||
for event in ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop']
|
for event in ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop']
|
||||||
$.on el, event.toLowerCase(), @[event]
|
$.on el, event.toLowerCase(), @[event]
|
||||||
|
|
||||||
|
@thread = if g.VIEW is 'thread'
|
||||||
|
g.THREADID
|
||||||
|
else
|
||||||
|
'new'
|
||||||
|
|
||||||
prev = QR.posts[QR.posts.length - 1]
|
prev = QR.posts[QR.posts.length - 1]
|
||||||
QR.posts.push @
|
QR.posts.push @
|
||||||
@spoiler = if prev and Conf['Remember Spoiler']
|
@nodes.spoiler.checked = @spoiler = if prev and Conf['Remember Spoiler']
|
||||||
prev.spoiler
|
prev.spoiler
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
@ -435,7 +441,7 @@ QR =
|
|||||||
lock: (lock=true) ->
|
lock: (lock=true) ->
|
||||||
@isLocked = lock
|
@isLocked = lock
|
||||||
return unless @ is QR.selected
|
return unless @ is QR.selected
|
||||||
for name in ['name', 'email', 'sub', 'com', 'spoiler']
|
for name in ['thread', 'name', 'email', 'sub', 'com', 'spoiler']
|
||||||
QR.nodes[name].disabled = lock
|
QR.nodes[name].disabled = lock
|
||||||
@nodes.rm.style.visibility =
|
@nodes.rm.style.visibility =
|
||||||
QR.nodes.fileRM.style.visibility = if lock then 'hidden' else ''
|
QR.nodes.fileRM.style.visibility = if lock then 'hidden' else ''
|
||||||
@ -461,12 +467,15 @@ QR =
|
|||||||
|
|
||||||
load: ->
|
load: ->
|
||||||
# Load this post's values.
|
# Load this post's values.
|
||||||
for name in ['name', 'email', 'sub', 'com']
|
for name in ['thread', 'name', 'email', 'sub', 'com']
|
||||||
QR.nodes[name].value = @[name] or null
|
QR.nodes[name].value = @[name] or null
|
||||||
@showFileData()
|
@showFileData()
|
||||||
QR.characterCount()
|
QR.characterCount()
|
||||||
|
|
||||||
save: (input) ->
|
save: (input) ->
|
||||||
|
if input.type is 'checkbox'
|
||||||
|
@spoiler = input.checked
|
||||||
|
return
|
||||||
{value} = input
|
{value} = input
|
||||||
@[input.dataset.name] = value
|
@[input.dataset.name] = value
|
||||||
return if input.nodeName isnt 'TEXTAREA'
|
return if input.nodeName isnt 'TEXTAREA'
|
||||||
@ -481,7 +490,7 @@ QR =
|
|||||||
return unless @ is QR.selected
|
return unless @ is QR.selected
|
||||||
# Do this in case people use extensions
|
# Do this in case people use extensions
|
||||||
# that do not trigger the `input` event.
|
# that do not trigger the `input` event.
|
||||||
for name in ['name', 'email', 'sub', 'com']
|
for name in ['thread', 'name', 'email', 'sub', 'com', 'spoiler']
|
||||||
@save QR.nodes[name]
|
@save QR.nodes[name]
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -565,8 +574,8 @@ QR =
|
|||||||
@showFileData()
|
@showFileData()
|
||||||
return unless window.URL
|
return unless window.URL
|
||||||
URL.revokeObjectURL @URL
|
URL.revokeObjectURL @URL
|
||||||
|
|
||||||
showFileData: (hide) ->
|
showFileData: ->
|
||||||
if @file
|
if @file
|
||||||
QR.nodes.filename.textContent = @filename
|
QR.nodes.filename.textContent = @filename
|
||||||
QR.nodes.filename.title = @filename
|
QR.nodes.filename.title = @filename
|
||||||
@ -661,6 +670,12 @@ QR =
|
|||||||
# start with an uncached captcha
|
# start with an uncached captcha
|
||||||
@reload()
|
@reload()
|
||||||
|
|
||||||
|
<% if (type === 'userscript') { %>
|
||||||
|
# XXX Firefox lacks focusin/focusout support.
|
||||||
|
$.on input, 'blur', QR.focusout
|
||||||
|
$.on input, 'focus', QR.focusin
|
||||||
|
<% } %>
|
||||||
|
|
||||||
$.addClass QR.nodes.el, 'has-captcha'
|
$.addClass QR.nodes.el, 'has-captcha'
|
||||||
$.after QR.nodes.dumpList.parentElement, [imgContainer, input]
|
$.after QR.nodes.dumpList.parentElement, [imgContainer, input]
|
||||||
|
|
||||||
@ -847,10 +862,17 @@ QR =
|
|||||||
$.add nodes.thread, $.el 'option',
|
$.add nodes.thread, $.el 'option',
|
||||||
value: thread
|
value: thread
|
||||||
textContent: "Thread No.#{thread}"
|
textContent: "Thread No.#{thread}"
|
||||||
QR.resetThreadSelector()
|
|
||||||
|
|
||||||
$.on nodes.filename.parentNode, 'click keyup', QR.openFileInput
|
$.on nodes.filename.parentNode, 'click keyup', QR.openFileInput
|
||||||
|
|
||||||
|
<% if (type === 'userscript') { %>
|
||||||
|
# XXX Firefox lacks focusin/focusout support.
|
||||||
|
for elm in $$ '*', QR.nodes.el
|
||||||
|
$.on elm, 'blur', QR.focusout
|
||||||
|
$.on elm, 'focus', QR.focusin
|
||||||
|
<% } %>
|
||||||
|
$.on QR.nodes.el, 'focusin', QR.focusin
|
||||||
|
$.on QR.nodes.el, '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'
|
||||||
@ -861,13 +883,21 @@ QR =
|
|||||||
QR.selected.rmFile()
|
QR.selected.rmFile()
|
||||||
$.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click()
|
$.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click()
|
||||||
$.on nodes.fileInput, 'change', QR.fileInput
|
$.on nodes.fileInput, 'change', QR.fileInput
|
||||||
|
|
||||||
new QR.post true
|
|
||||||
# save selected post's data
|
# save selected post's data
|
||||||
for name in ['name', 'email', 'sub', 'com']
|
for name in ['name', 'email', 'sub', 'com']
|
||||||
$.on nodes[name], 'input', -> QR.selected.save @
|
$.on nodes[name], 'input', -> QR.selected.save @
|
||||||
$.on nodes[name], 'focus', -> $.addClass nodes.el, 'focus'
|
$.on nodes.thread, 'change', -> QR.selected.save @
|
||||||
$.on nodes[name], 'blur', -> $.rmClass nodes.el, 'focus'
|
|
||||||
|
<% if (type === 'userscript') { %>
|
||||||
|
if Conf['Remember QR Size']
|
||||||
|
$.get 'QR Size', '', (item) ->
|
||||||
|
nodes.com.style.cssText = item['QR Size']
|
||||||
|
$.on nodes.com, 'mouseup', (e) ->
|
||||||
|
return if e.button isnt 0
|
||||||
|
$.set 'QR Size', @style.cssText
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
new QR.post true
|
||||||
|
|
||||||
QR.status()
|
QR.status()
|
||||||
QR.cooldown.init()
|
QR.cooldown.init()
|
||||||
@ -897,7 +927,7 @@ QR =
|
|||||||
post.forceSave()
|
post.forceSave()
|
||||||
if g.BOARD.ID is 'f'
|
if g.BOARD.ID is 'f'
|
||||||
filetag = QR.nodes.flashTag.value
|
filetag = QR.nodes.flashTag.value
|
||||||
threadID = QR.nodes.thread.value
|
threadID = post.thread
|
||||||
thread = g.BOARD.threads[threadID]
|
thread = g.BOARD.threads[threadID]
|
||||||
|
|
||||||
# prevent errors
|
# prevent errors
|
||||||
@ -985,6 +1015,11 @@ QR =
|
|||||||
QR.status()
|
QR.status()
|
||||||
|
|
||||||
response: ->
|
response: ->
|
||||||
|
<% if (type === 'userjs') { %>
|
||||||
|
# The upload.onload callback is not called
|
||||||
|
# or at least not in time with Opera.
|
||||||
|
QR.req.upload.onload()
|
||||||
|
<% } %>
|
||||||
{req} = QR
|
{req} = QR
|
||||||
delete QR.req
|
delete QR.req
|
||||||
|
|
||||||
@ -1026,6 +1061,12 @@ QR =
|
|||||||
# Too many frequent mistyped captchas will auto-ban you!
|
# Too many frequent mistyped captchas will auto-ban you!
|
||||||
# On connection error, the post most likely didn't go through.
|
# On connection error, the post most likely didn't go through.
|
||||||
QR.cooldown.set delay: 2
|
QR.cooldown.set delay: 2
|
||||||
|
else if err.textContent and m = err.textContent.match /wait\s(\d+)\ssecond/i
|
||||||
|
QR.cooldown.auto = if QR.captcha.isEnabled
|
||||||
|
!!QR.captcha.captchas.length
|
||||||
|
else
|
||||||
|
true
|
||||||
|
QR.cooldown.set delay: m[1]
|
||||||
else # stop auto-posting
|
else # stop auto-posting
|
||||||
QR.cooldown.auto = false
|
QR.cooldown.auto = false
|
||||||
QR.status()
|
QR.status()
|
||||||
|
|||||||
@ -124,7 +124,7 @@ Settings =
|
|||||||
$.rmClass selected, 'tab-selected'
|
$.rmClass selected, 'tab-selected'
|
||||||
$.addClass $(".tab-#{@hyphenatedTitle}", Settings.dialog), 'tab-selected'
|
$.addClass $(".tab-#{@hyphenatedTitle}", Settings.dialog), 'tab-selected'
|
||||||
section = $ 'section', Settings.dialog
|
section = $ 'section', Settings.dialog
|
||||||
section.innerHTML = null
|
$.rmAll section
|
||||||
section.className = "section-#{@hyphenatedTitle}"
|
section.className = "section-#{@hyphenatedTitle}"
|
||||||
@open section, mode
|
@open section, mode
|
||||||
section.scrollTop = 0
|
section.scrollTop = 0
|
||||||
@ -188,6 +188,13 @@ Settings =
|
|||||||
$.delete ['hiddenThreads', 'hiddenPosts']
|
$.delete ['hiddenThreads', 'hiddenPosts']
|
||||||
$.after $('input[name="Stubs"]', section).parentNode.parentNode, div
|
$.after $('input[name="Stubs"]', section).parentNode.parentNode, div
|
||||||
|
|
||||||
|
div = $.el 'div',
|
||||||
|
innerHTML: "<button>Reset Header</button><span class=description>: Unhide the navigation bar."
|
||||||
|
unhide = $ 'button', div
|
||||||
|
$.on unhide, 'click', ->
|
||||||
|
Header.setBarPosition.call textContent: "sticky top"
|
||||||
|
$.after $('input[name="Check for Updates"]', section).parentNode.parentNode, div
|
||||||
|
|
||||||
export: (now, data) ->
|
export: (now, data) ->
|
||||||
unless typeof now is 'number'
|
unless typeof now is 'number'
|
||||||
now = Date.now()
|
now = Date.now()
|
||||||
@ -213,7 +220,7 @@ Settings =
|
|||||||
return
|
return
|
||||||
# XXX Firefox won't let us download automatically.
|
# XXX Firefox won't let us download automatically.
|
||||||
p = $ '.imp-exp-result', Settings.dialog
|
p = $ '.imp-exp-result', Settings.dialog
|
||||||
p.innerHTML = null
|
$.rmAll p
|
||||||
$.add p, a
|
$.add p, a
|
||||||
|
|
||||||
import: ->
|
import: ->
|
||||||
@ -340,7 +347,7 @@ Settings =
|
|||||||
selectFilter: ->
|
selectFilter: ->
|
||||||
div = @nextElementSibling
|
div = @nextElementSibling
|
||||||
if (name = @value) isnt 'guide'
|
if (name = @value) isnt 'guide'
|
||||||
div.innerHTML = null
|
$.rmAll div
|
||||||
ta = $.el 'textarea',
|
ta = $.el 'textarea',
|
||||||
name: name
|
name: name
|
||||||
className: 'field'
|
className: 'field'
|
||||||
@ -824,7 +831,7 @@ Settings =
|
|||||||
mascotHide = $.el "div",
|
mascotHide = $.el "div",
|
||||||
id: "mascot_hide"
|
id: "mascot_hide"
|
||||||
className: "reply"
|
className: "reply"
|
||||||
innerHTML: "Hide Categories <span class=dropmarker></span><div></div>"
|
innerHTML: "Hide Categories <span class=drop-marker></span><div></div>"
|
||||||
|
|
||||||
keys = Object.keys Mascots
|
keys = Object.keys Mascots
|
||||||
keys.sort()
|
keys.sort()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user