Merge remote-tracking branch 'mayhem/v3' into v3
Conflicts: .gitattributes 4chan-X.meta.js 4chan-X.user.js Gruntfile.js lib/$.coffee src/config.coffee src/features.coffee src/globals.coffee
@ -3,10 +3,10 @@
|
|||||||
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.
|
||||||
3. If your issue persists, open a [new issue](https://github.com/MayhemYDG/4chan-x/issues) with the following information:
|
3. If your issue persists, open a [new issue](https://github.com/MayhemYDG/4chan-x/issues) with the following information:
|
||||||
1. Report precise steps to reproduce the problem.
|
1. Precise steps to reproduce the problem.
|
||||||
2. Report console errors, if any.
|
2. Console errors, if any.
|
||||||
3. Report browser version.
|
3. Browser version.
|
||||||
4. Include your exported settings.
|
4. Your exported settings.
|
||||||
|
|
||||||
Open your console with:
|
Open your console with:
|
||||||
- `Ctrl + Shift + J` on Chrome.
|
- `Ctrl + Shift + J` on Chrome.
|
||||||
|
|||||||
124
Gruntfile.coffee
@ -23,31 +23,18 @@ module.exports = (grunt) ->
|
|||||||
]
|
]
|
||||||
dest: 'tmp/script.coffee'
|
dest: 'tmp/script.coffee'
|
||||||
|
|
||||||
manifest:
|
|
||||||
options:
|
|
||||||
process:
|
|
||||||
data: pkg
|
|
||||||
src: 'src/manifest.json',
|
|
||||||
dest: 'builds/crx/manifest.json'
|
|
||||||
|
|
||||||
metadata:
|
|
||||||
options:
|
|
||||||
process:
|
|
||||||
data: pkg
|
|
||||||
src: 'src/metadata.js',
|
|
||||||
dest: '<%= pkg.name %>.meta.js'
|
|
||||||
|
|
||||||
crx:
|
crx:
|
||||||
options:
|
options:
|
||||||
process:
|
process:
|
||||||
data: pkg
|
data: pkg
|
||||||
src: [
|
files:
|
||||||
'src/banner.js'
|
'builds/crx/manifest.json': 'src/manifest.json'
|
||||||
'tmp/script.js'
|
'builds/crx/script.js': [
|
||||||
]
|
'src/banner.js'
|
||||||
dest: 'builds/crx/script.js'
|
'tmp/script.js'
|
||||||
|
]
|
||||||
|
|
||||||
userscript:
|
userjs:
|
||||||
options:
|
options:
|
||||||
process:
|
process:
|
||||||
data: pkg
|
data: pkg
|
||||||
@ -56,13 +43,27 @@ module.exports = (grunt) ->
|
|||||||
'src/banner.js'
|
'src/banner.js'
|
||||||
'tmp/script.js'
|
'tmp/script.js'
|
||||||
]
|
]
|
||||||
dest: '<%= pkg.name %>.user.js'
|
|
||||||
|
|
||||||
userjs:
|
|
||||||
# Lazily copy the userscript
|
|
||||||
src: '<%= pkg.name %>.user.js'
|
|
||||||
dest: 'builds/<%= pkg.name %>.js'
|
dest: 'builds/<%= pkg.name %>.js'
|
||||||
|
|
||||||
|
userscript:
|
||||||
|
options:
|
||||||
|
process:
|
||||||
|
data: pkg
|
||||||
|
files:
|
||||||
|
'builds/<%= pkg.name %>.meta.js': 'src/metadata.js'
|
||||||
|
'builds/<%= pkg.name %>.user.js': [
|
||||||
|
'src/metadata.js'
|
||||||
|
'src/banner.js'
|
||||||
|
'tmp/script.js'
|
||||||
|
]
|
||||||
|
|
||||||
|
copy:
|
||||||
|
crx:
|
||||||
|
src: 'img/*.png'
|
||||||
|
dest: 'builds/crx/'
|
||||||
|
expand: true
|
||||||
|
flatten: true
|
||||||
|
|
||||||
coffee:
|
coffee:
|
||||||
script:
|
script:
|
||||||
src: 'tmp/script.coffee'
|
src: 'tmp/script.coffee'
|
||||||
@ -89,35 +90,73 @@ module.exports = (grunt) ->
|
|||||||
options:
|
options:
|
||||||
interrupt: true
|
interrupt: true
|
||||||
files: [
|
files: [
|
||||||
'Gruntfile.js'
|
'Gruntfile.coffee'
|
||||||
'package.json'
|
'package.json'
|
||||||
'lib/**/*.coffee'
|
'lib/**/*'
|
||||||
'src/**/*.coffee'
|
'src/**/*'
|
||||||
'src/**/*.js'
|
'css/**/*'
|
||||||
'css/**/*.css'
|
'img/**/*'
|
||||||
'img/*'
|
|
||||||
]
|
]
|
||||||
tasks: 'default'
|
tasks: 'build'
|
||||||
|
|
||||||
|
compress:
|
||||||
|
crx:
|
||||||
|
options:
|
||||||
|
archive: 'builds/4chan-X.zip'
|
||||||
|
level: 9
|
||||||
|
pretty: true
|
||||||
|
expand: true
|
||||||
|
cwd: 'builds/crx/'
|
||||||
|
src: '**'
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
tmp: 'tmp'
|
builds: 'builds'
|
||||||
|
tmp: 'tmp'
|
||||||
|
|
||||||
grunt.loadNpmTasks 'grunt-bump'
|
grunt.loadNpmTasks 'grunt-bump'
|
||||||
grunt.loadNpmTasks 'grunt-contrib-clean'
|
grunt.loadNpmTasks 'grunt-contrib-clean'
|
||||||
grunt.loadNpmTasks 'grunt-contrib-coffee'
|
grunt.loadNpmTasks 'grunt-contrib-coffee'
|
||||||
|
grunt.loadNpmTasks 'grunt-contrib-compress'
|
||||||
grunt.loadNpmTasks 'grunt-contrib-concat'
|
grunt.loadNpmTasks 'grunt-contrib-concat'
|
||||||
|
grunt.loadNpmTasks 'grunt-contrib-copy'
|
||||||
grunt.loadNpmTasks 'grunt-contrib-watch'
|
grunt.loadNpmTasks 'grunt-contrib-watch'
|
||||||
grunt.loadNpmTasks 'grunt-exec'
|
grunt.loadNpmTasks 'grunt-exec'
|
||||||
|
|
||||||
grunt.registerTask 'default', [
|
grunt.registerTask 'default', ['build']
|
||||||
'concat:coffee',
|
|
||||||
'coffee:script',
|
grunt.registerTask 'set-build', 'Set the build type variable', (type) ->
|
||||||
'concat:manifest',
|
pkg.type = type;
|
||||||
'concat:crx',
|
grunt.log.ok 'pkg.type = %s', type
|
||||||
'concat:userscript',
|
|
||||||
'concat:userjs',
|
grunt.registerTask 'build', [
|
||||||
'concat:metadata',
|
'build-crx'
|
||||||
'clean'
|
'build-userjs'
|
||||||
|
'build-userscript'
|
||||||
|
]
|
||||||
|
|
||||||
|
grunt.registerTask 'build-crx', [
|
||||||
|
'set-build:crx'
|
||||||
|
'concat:coffee'
|
||||||
|
'coffee:script'
|
||||||
|
'concat:crx'
|
||||||
|
'copy:crx'
|
||||||
|
'clean:tmp'
|
||||||
|
]
|
||||||
|
|
||||||
|
grunt.registerTask 'build-userjs', [
|
||||||
|
'set-build:userjs'
|
||||||
|
'concat:coffee'
|
||||||
|
'coffee:script'
|
||||||
|
'concat:userjs'
|
||||||
|
'clean:tmp'
|
||||||
|
]
|
||||||
|
|
||||||
|
grunt.registerTask 'build-userscript', [
|
||||||
|
'set-build:userscript'
|
||||||
|
'concat:coffee'
|
||||||
|
'coffee:script'
|
||||||
|
'concat:userscript'
|
||||||
|
'clean:tmp'
|
||||||
]
|
]
|
||||||
|
|
||||||
grunt.registerTask 'release', [
|
grunt.registerTask 'release', [
|
||||||
@ -140,6 +179,7 @@ module.exports = (grunt) ->
|
|||||||
'bump:major'
|
'bump:major'
|
||||||
'updcl:1'
|
'updcl:1'
|
||||||
]
|
]
|
||||||
|
|
||||||
grunt.registerTask 'updcl', 'Update the changelog', (i) ->
|
grunt.registerTask 'updcl', 'Update the changelog', (i) ->
|
||||||
# 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');
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
# 4chan X
|
# 4chan X
|
||||||
|
|
||||||
Get it [here](http://mayhemydg.github.com/4chan-x/).
|
Get it [here](https://4chan-x.just-believe.in/).
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
### [MIT License](/4chan-x/blob/master/LICENSE)
|
### [MIT License](/LICENSE)
|
||||||
### [Contribute](/4chan-x/blob/master/CONTRIBUTING.md)
|
### [Contribute](/CONTRIBUTING.md)
|
||||||
|
|||||||
@ -60,7 +60,7 @@ a[href="javascript:;"] {
|
|||||||
#qp, #ihover,
|
#qp, #ihover,
|
||||||
#updater, #thread-stats,
|
#updater, #thread-stats,
|
||||||
#navlinks, #header,
|
#navlinks, #header,
|
||||||
#qr, #watcher {
|
#qr {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
}
|
}
|
||||||
#overlay {
|
#overlay {
|
||||||
@ -81,12 +81,15 @@ a[href="javascript:;"] {
|
|||||||
#qr {
|
#qr {
|
||||||
z-index: 30;
|
z-index: 30;
|
||||||
}
|
}
|
||||||
#watcher {
|
#watcher:hover {
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
}
|
}
|
||||||
#header {
|
#header {
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
#watcher {
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
.fourchan-x body {
|
.fourchan-x body {
|
||||||
@ -104,8 +107,8 @@ a[href="javascript:;"] {
|
|||||||
display: flex;
|
display: flex;
|
||||||
padding: 3px 4px 4px;
|
padding: 3px 4px 4px;
|
||||||
position: relative;
|
position: relative;
|
||||||
-webkit-transition: all .1s ease-in-out;
|
-webkit-transition: all .1s .05s ease-in-out;
|
||||||
transition: all .1s ease-in-out;
|
transition: all .1s .05s ease-in-out;
|
||||||
}
|
}
|
||||||
#board-list {
|
#board-list {
|
||||||
-webkit-flex: 1;
|
-webkit-flex: 1;
|
||||||
@ -252,6 +255,9 @@ a[href="javascript:;"] {
|
|||||||
-webkit-flex: 1;
|
-webkit-flex: 1;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
.tab-selected {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
.section-container {
|
.section-container {
|
||||||
-webkit-flex: 1;
|
-webkit-flex: 1;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@ -421,7 +427,7 @@ a[href="javascript:;"] {
|
|||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
}
|
}
|
||||||
.qphl {
|
.qphl > .post {
|
||||||
outline: 2px solid rgba(216, 94, 49, .7);
|
outline: 2px solid rgba(216, 94, 49, .7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,6 +441,9 @@ a[href="javascript:;"] {
|
|||||||
.expanding {
|
.expanding {
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
}
|
}
|
||||||
|
.expanded-image {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
.expanded-image > .op > .file::after {
|
.expanded-image > .op > .file::after {
|
||||||
content: '';
|
content: '';
|
||||||
clear: both;
|
clear: both;
|
||||||
@ -736,6 +745,11 @@ a[href="javascript:;"] {
|
|||||||
#file-n-submit > #qr-file-spoiler {
|
#file-n-submit > #qr-file-spoiler {
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
|
#file-n-submit input[type='submit'] {
|
||||||
|
min-width: 40px;
|
||||||
|
-webkit-order: 1;
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
#qr input[type='file'] {
|
#qr input[type='file'] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 450 B |
|
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 450 B |
|
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 232 B |
|
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 232 B |
|
Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 232 B |
|
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 274 B |
|
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 274 B |
|
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 270 B |
|
Before Width: | Height: | Size: 420 B After Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 452 B After Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 455 B After Width: | Height: | Size: 349 B |
BIN
img/icon128.png
Normal file
|
After Width: | Height: | Size: 196 B |
BIN
img/icon16.png
Normal file
|
After Width: | Height: | Size: 157 B |
BIN
img/icon48.png
Normal file
|
After Width: | Height: | Size: 204 B |
84
lib/$.coffee
@ -51,7 +51,7 @@ $.extend String::,
|
|||||||
$.DAY = 24 * ($.HOUR = 60 * ($.MINUTE = 60 * ($.SECOND = 1000)))
|
$.DAY = 24 * ($.HOUR = 60 * ($.MINUTE = 60 * ($.SECOND = 1000)))
|
||||||
|
|
||||||
$.extend $,
|
$.extend $,
|
||||||
engine: /WebKit|Presto|Gecko/.exec(navigator.userAgent)[0].toLowerCase()
|
engine: '<% if (type === 'crx') { %>webkit<% } else if (type === 'userjs') { %>presto<% } else { %>gecko<% } %>'
|
||||||
id: (id) ->
|
id: (id) ->
|
||||||
d.getElementById id
|
d.getElementById id
|
||||||
ready: (fc) ->
|
ready: (fc) ->
|
||||||
@ -226,19 +226,8 @@ $.extend $,
|
|||||||
globalEval: (code) ->
|
globalEval: (code) ->
|
||||||
script = $.el 'script',
|
script = $.el 'script',
|
||||||
textContent: code
|
textContent: code
|
||||||
$.add d.head, script
|
$.add (d.head or doc), script
|
||||||
$.rm script
|
$.rm script
|
||||||
# http://mths.be/unsafewindow
|
|
||||||
unsafeWindow:
|
|
||||||
if window.opera # Opera
|
|
||||||
window
|
|
||||||
else if unsafeWindow? # Firefox
|
|
||||||
unsafeWindow
|
|
||||||
else # Chrome
|
|
||||||
do ->
|
|
||||||
p = d.createElement 'p'
|
|
||||||
p.setAttribute 'onclick', 'return window'
|
|
||||||
p.onclick()
|
|
||||||
bytesToString: (size) ->
|
bytesToString: (size) ->
|
||||||
unit = 0 # Bytes
|
unit = 0 # Bytes
|
||||||
while size >= 1024
|
while size >= 1024
|
||||||
@ -255,11 +244,30 @@ $.extend $,
|
|||||||
Math.round size
|
Math.round size
|
||||||
"#{size} #{['B', 'KB', 'MB', 'GB'][unit]}"
|
"#{size} #{['B', 'KB', 'MB', 'GB'][unit]}"
|
||||||
|
|
||||||
if GM_deleteValue?
|
<% if (type === 'crx') { %>
|
||||||
|
delete: (name) ->
|
||||||
|
localStorage.removeItem g.NAMESPACE + name
|
||||||
|
get: (name, defaultValue) ->
|
||||||
|
if value = localStorage.getItem g.NAMESPACE + name
|
||||||
|
JSON.parse value
|
||||||
|
else
|
||||||
|
defaultValue
|
||||||
|
set: (name, value) ->
|
||||||
|
localStorage.setItem g.NAMESPACE + name, JSON.stringify value
|
||||||
|
<% } else if (type === 'userjs') { %>
|
||||||
|
do ->
|
||||||
|
# http://www.opera.com/docs/userjs/specs/#scriptstorage
|
||||||
|
# http://www.opera.com/docs/userjs/using/#securepages
|
||||||
|
# The scriptStorage object is available only during
|
||||||
|
# the main User JavaScript thread, being therefore
|
||||||
|
# accessible only in the main body of the user script.
|
||||||
|
# To access the storage object later, keep a reference
|
||||||
|
# to the object.
|
||||||
|
{scriptStorage} = opera
|
||||||
$.delete = (name) ->
|
$.delete = (name) ->
|
||||||
GM_deleteValue g.NAMESPACE + name
|
delete scriptStorage[g.NAMESPACE + name]
|
||||||
$.get = (name, defaultValue) ->
|
$.get = (name, defaultValue) ->
|
||||||
if value = GM_getValue g.NAMESPACE + name
|
if value = scriptStorage[g.NAMESPACE + name]
|
||||||
JSON.parse value
|
JSON.parse value
|
||||||
else
|
else
|
||||||
defaultValue
|
defaultValue
|
||||||
@ -268,37 +276,19 @@ if GM_deleteValue?
|
|||||||
value = JSON.stringify value
|
value = JSON.stringify value
|
||||||
# for `storage` events
|
# for `storage` events
|
||||||
localStorage.setItem name, value
|
localStorage.setItem name, value
|
||||||
GM_setValue name, value
|
scriptStorage[name] = value
|
||||||
else if window.opera
|
<% } else { %>
|
||||||
do ->
|
delete: (name) ->
|
||||||
# http://www.opera.com/docs/userjs/specs/#scriptstorage
|
GM_deleteValue g.NAMESPACE + name
|
||||||
# http://www.opera.com/docs/userjs/using/#securepages
|
get: (name, defaultValue) ->
|
||||||
# >The scriptStorage object is available only during
|
if value = GM_getValue g.NAMESPACE + name
|
||||||
# the main User JavaScript thread, being therefore
|
|
||||||
# accessible only in the main body of the user script.
|
|
||||||
# To access the storage object later, keep a reference
|
|
||||||
# to the object.
|
|
||||||
{scriptStorage} = opera
|
|
||||||
$.delete = (name) ->
|
|
||||||
delete scriptStorage[g.NAMESPACE + name]
|
|
||||||
$.get = (name, defaultValue) ->
|
|
||||||
if value = scriptStorage[g.NAMESPACE + name]
|
|
||||||
JSON.parse value
|
|
||||||
else
|
|
||||||
defaultValue
|
|
||||||
$.set = (name, value) ->
|
|
||||||
name = g.NAMESPACE + name
|
|
||||||
value = JSON.stringify value
|
|
||||||
# for `storage` events
|
|
||||||
localStorage.setItem name, value
|
|
||||||
scriptStorage[name] = value
|
|
||||||
else
|
|
||||||
$.delete = (name) ->
|
|
||||||
localStorage.removeItem g.NAMESPACE + name
|
|
||||||
$.get = (name, defaultValue) ->
|
|
||||||
if value = localStorage.getItem g.NAMESPACE + name
|
|
||||||
JSON.parse value
|
JSON.parse value
|
||||||
else
|
else
|
||||||
defaultValue
|
defaultValue
|
||||||
$.set = (name, value) ->
|
set: (name, value) ->
|
||||||
localStorage.setItem g.NAMESPACE + name, JSON.stringify value
|
name = g.NAMESPACE + name
|
||||||
|
value = JSON.stringify value
|
||||||
|
# for `storage` events
|
||||||
|
localStorage.setItem name, value
|
||||||
|
GM_setValue name, value
|
||||||
|
<% } %>
|
||||||
|
|||||||
12
package.json
@ -5,7 +5,7 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"name": "4chan X Beta",
|
"name": "4chan X Beta",
|
||||||
"repo": "https://github.com/MayhemYDG/4chan-x/",
|
"repo": "https://github.com/MayhemYDG/4chan-x/",
|
||||||
"page": "http://mayhemydg.github.com/4chan-x/",
|
"page": "https://4chan-x.just-believe.in/",
|
||||||
"mainBranch": "v3",
|
"mainBranch": "v3",
|
||||||
"matches": [
|
"matches": [
|
||||||
"*://api.4chan.org/*",
|
"*://api.4chan.org/*",
|
||||||
@ -15,12 +15,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"grunt": "~0.4.0",
|
"grunt": "~0.4.1",
|
||||||
"grunt-bump": "~0.0.0",
|
"grunt-bump": "~0.0.0",
|
||||||
"grunt-contrib-clean": "~0.4.0",
|
"grunt-contrib-clean": "~0.4.0",
|
||||||
"grunt-contrib-coffee": "~0.6.2",
|
"grunt-contrib-coffee": "~0.6.4",
|
||||||
"grunt-contrib-concat": "~0.1.0",
|
"grunt-contrib-compress": "~0.4.5",
|
||||||
"grunt-contrib-watch": "~0.3.0",
|
"grunt-contrib-concat": "~0.1.3",
|
||||||
|
"grunt-contrib-copy": "~0.4.0",
|
||||||
|
"grunt-contrib-watch": "~0.3.1",
|
||||||
"grunt-exec": "~0.4.0"
|
"grunt-exec": "~0.4.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@ -15,7 +15,7 @@ Config =
|
|||||||
]
|
]
|
||||||
'Custom Board Navigation': [
|
'Custom Board Navigation': [
|
||||||
true
|
true
|
||||||
'Disable this to always display the full board list.'
|
'Show custom links instead of the full board list.'
|
||||||
]
|
]
|
||||||
'404 Redirect': [
|
'404 Redirect': [
|
||||||
true
|
true
|
||||||
@ -27,7 +27,7 @@ Config =
|
|||||||
]
|
]
|
||||||
'Time Formatting': [
|
'Time Formatting': [
|
||||||
true
|
true
|
||||||
'Localize and format timestamps arbitrarily.'
|
'Localize and format timestamps.'
|
||||||
]
|
]
|
||||||
'Relative Post Dates': [
|
'Relative Post Dates': [
|
||||||
false
|
false
|
||||||
@ -39,19 +39,15 @@ Config =
|
|||||||
]
|
]
|
||||||
'Comment Expansion': [
|
'Comment Expansion': [
|
||||||
true
|
true
|
||||||
'Can expand too long comments.'
|
'Add buttons to expand long comments.'
|
||||||
]
|
]
|
||||||
'Thread Expansion': [
|
'Thread Expansion': [
|
||||||
true
|
true
|
||||||
'Can expand threads to view all replies.'
|
'Add buttons to expand threads.'
|
||||||
]
|
]
|
||||||
'Index Navigation': [
|
'Index Navigation': [
|
||||||
false
|
false
|
||||||
'Navigate to previous / next thread.'
|
'Add buttons to navigate between threads.'
|
||||||
]
|
|
||||||
'Custom CSS': [
|
|
||||||
false
|
|
||||||
'Apply custom CSS to 4chan.'
|
|
||||||
]
|
]
|
||||||
'Check for Updates': [
|
'Check for Updates': [
|
||||||
true
|
true
|
||||||
@ -75,7 +71,7 @@ Config =
|
|||||||
'Filtering':
|
'Filtering':
|
||||||
'Anonymize': [
|
'Anonymize': [
|
||||||
false
|
false
|
||||||
'Turn everyone Anonymous.'
|
'Make everyone Anonymous.'
|
||||||
]
|
]
|
||||||
'Filter': [
|
'Filter': [
|
||||||
true
|
true
|
||||||
@ -87,19 +83,19 @@ Config =
|
|||||||
]
|
]
|
||||||
'Thread Hiding': [
|
'Thread Hiding': [
|
||||||
true
|
true
|
||||||
'Hide entire threads.'
|
'Add buttons to hide entire threads.'
|
||||||
]
|
]
|
||||||
'Reply Hiding': [
|
'Reply Hiding': [
|
||||||
true
|
true
|
||||||
'Hide single replies.'
|
'Add buttons to hide single replies.'
|
||||||
]
|
]
|
||||||
'Hiding Buttons': [
|
'Hiding Buttons': [
|
||||||
true
|
true
|
||||||
'Make buttons to hide threads / replies, in addition to menu links.'
|
'Add buttons to hide threads / replies, in addition to menu links.'
|
||||||
]
|
]
|
||||||
'Stubs': [
|
'Stubs': [
|
||||||
true
|
true
|
||||||
'Make stubs of hidden threads / replies.'
|
'Show stubs of hidden threads / replies.'
|
||||||
]
|
]
|
||||||
|
|
||||||
'Images':
|
'Images':
|
||||||
@ -135,8 +131,16 @@ Config =
|
|||||||
'Menu':
|
'Menu':
|
||||||
'Menu': [
|
'Menu': [
|
||||||
true
|
true
|
||||||
'Add a drop-down menu in posts.'
|
'Add a drop-down menu to posts.'
|
||||||
]
|
]
|
||||||
|
'Thread Hiding Link': [
|
||||||
|
true
|
||||||
|
'Add a link to hide entire threads.'
|
||||||
|
]
|
||||||
|
'Reply Hiding Link': [
|
||||||
|
true
|
||||||
|
'Add a link to hide single replies.'
|
||||||
|
]
|
||||||
'Report Link': [
|
'Report Link': [
|
||||||
true
|
true
|
||||||
'Add a report link to the menu.'
|
'Add a report link to the menu.'
|
||||||
@ -222,7 +226,7 @@ Config =
|
|||||||
'Hide the normal post form.'
|
'Hide the normal post form.'
|
||||||
]
|
]
|
||||||
|
|
||||||
'Quote links':
|
'Quote Links':
|
||||||
'Quote Backlinks': [
|
'Quote Backlinks': [
|
||||||
true
|
true
|
||||||
'Add quote backlinks.'
|
'Add quote backlinks.'
|
||||||
@ -331,21 +335,23 @@ Config =
|
|||||||
MD5: ''
|
MD5: ''
|
||||||
|
|
||||||
sauces: """
|
sauces: """
|
||||||
http://iqdb.org/?url=%turl
|
http://iqdb.org/?url=%TURL
|
||||||
http://www.google.com/searchbyimage?image_url=%turl
|
https://www.google.com/searchbyimage?image_url=%TURL
|
||||||
#http://tineye.com/search?url=%turl
|
#//tineye.com/search?url=%TURL
|
||||||
#http://saucenao.com/search.php?db=999&url=%turl
|
#http://saucenao.com/search.php?url=%TURL
|
||||||
#http://3d.iqdb.org/?url=%turl
|
#http://3d.iqdb.org/?url=%TURL
|
||||||
#http://regex.info/exif.cgi?imgurl=%url
|
#http://regex.info/exif.cgi?imgurl=%URL
|
||||||
# uploaders:
|
# uploaders:
|
||||||
#http://imgur.com/upload?url=%url;text:Upload to imgur
|
#http://imgur.com/upload?url=%URL;text:Upload to imgur
|
||||||
#http://omploader.org/upload?url1=%url;text:Upload to omploader
|
#http://ompldr.org/upload?url1=%URL;text:Upload to ompldr
|
||||||
# "View Same" in archives:
|
# "View Same" in archives:
|
||||||
#//archive.foolz.us/_/search/image/%MD5/;text:View same on foolz
|
#//archive.foolz.us/_/search/image/%MD5/;text:View same on foolz
|
||||||
#//archive.foolz.us/%board/search/image/%MD5/;text:View same on foolz /%board/
|
#//archive.foolz.us/%board/search/image/%MD5/;text:View same on foolz /%board/
|
||||||
#//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
|
||||||
|
|
||||||
'Header auto-hide': false
|
'Header auto-hide': false
|
||||||
|
|
||||||
'Header catalog links': false
|
'Header catalog links': false
|
||||||
|
|||||||
@ -31,7 +31,7 @@ Header =
|
|||||||
className: 'hide-board-list-button brackets-wrap'
|
className: 'hide-board-list-button brackets-wrap'
|
||||||
innerHTML: '<a href=javascript:;> - </a>'
|
innerHTML: '<a href=javascript:;> - </a>'
|
||||||
$.on btn, 'click', Header.toggleBoardList
|
$.on btn, 'click', Header.toggleBoardList
|
||||||
$.prepend fullBoardList, btn
|
$.add fullBoardList, btn
|
||||||
else
|
else
|
||||||
$.rm $ '#custom-board-list', nav
|
$.rm $ '#custom-board-list', nav
|
||||||
fullBoardList.hidden = false
|
fullBoardList.hidden = false
|
||||||
@ -40,8 +40,8 @@ Header =
|
|||||||
list = $ '#custom-board-list', Header.nav
|
list = $ '#custom-board-list', Header.nav
|
||||||
list.innerHTML = null
|
list.innerHTML = null
|
||||||
return unless text
|
return unless text
|
||||||
as = $$('#full-board-list a', Header.nav)
|
as = $$('#full-board-list a', Header.nav)[0...-2] # ignore the Settings and Home links
|
||||||
nodes = text.match(/[\w@]+(-(all|title|full|text:"[^"]+"))?|[^\w@]+/g).map (t) ->
|
nodes = text.match(/[\w@]+(-(all|title|full|index|catalog|text:"[^"]+"))*|[^\w@]+/g).map (t) ->
|
||||||
if /^[^\w@]/.test t
|
if /^[^\w@]/.test t
|
||||||
return $.tn t
|
return $.tn t
|
||||||
if t is 'toggle-all'
|
if t is 'toggle-all'
|
||||||
@ -58,12 +58,17 @@ Header =
|
|||||||
for a in as
|
for a in as
|
||||||
if a.textContent is board
|
if a.textContent is board
|
||||||
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 /-full$/.test t
|
else if /-full/.test t
|
||||||
a.textContent = "/#{board}/ - #{a.title}"
|
a.textContent = "/#{board}/ - #{a.title}"
|
||||||
else if m = t.match /-text:"(.+)"$/
|
else if /-(index|catalog|text)/.test t
|
||||||
a.textContent = m[1]
|
if m = t.match /-(index|catalog)/
|
||||||
|
a.setAttribute 'data-only', m[1]
|
||||||
|
a.href = "//boards.4chan.org/#{board}/"
|
||||||
|
a.href += 'catalog' if m[1] is 'catalog'
|
||||||
|
if m = t.match /-text:"(.+)"/
|
||||||
|
a.textContent = m[1]
|
||||||
else if board is '@'
|
else if board is '@'
|
||||||
$.addClass a, 'navSmall'
|
$.addClass a, 'navSmall'
|
||||||
return a
|
return a
|
||||||
@ -224,17 +229,15 @@ Settings =
|
|||||||
links = []
|
links = []
|
||||||
for section in Settings.sections
|
for section in Settings.sections
|
||||||
link = $.el 'a',
|
link = $.el 'a',
|
||||||
|
className: "tab-#{section.hyphenatedTitle}"
|
||||||
textContent: section.title
|
textContent: section.title
|
||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
$.on link, 'click', Settings.openSection.bind section
|
$.on link, 'click', Settings.openSection.bind section
|
||||||
links.push link, $.tn ' | '
|
links.push link, $.tn ' | '
|
||||||
sectionToOpen = link if section.title is openSection
|
sectionToOpen = link if section.title is openSection
|
||||||
links.pop()
|
links.pop()
|
||||||
if sectionToOpen
|
|
||||||
sectionToOpen.click()
|
|
||||||
else
|
|
||||||
links[0].click()
|
|
||||||
$.add $('.sections-list', overlay), links
|
$.add $('.sections-list', overlay), links
|
||||||
|
(if sectionToOpen then sectionToOpen else links[0]).click()
|
||||||
|
|
||||||
$.on $('.close', overlay), 'click', Settings.close
|
$.on $('.close', overlay), 'click', Settings.close
|
||||||
$.on overlay, 'click', Settings.close
|
$.on overlay, 'click', Settings.close
|
||||||
@ -254,11 +257,15 @@ Settings =
|
|||||||
addSection: (title, open) ->
|
addSection: (title, open) ->
|
||||||
if typeof title isnt 'string'
|
if typeof title isnt 'string'
|
||||||
{title, open} = title.detail
|
{title, open} = title.detail
|
||||||
Settings.sections.push {title, open}
|
hyphenatedTitle = title.toLowerCase().replace /\s+/g, '-'
|
||||||
|
Settings.sections.push {title, hyphenatedTitle, open}
|
||||||
openSection: ->
|
openSection: ->
|
||||||
|
if selected = $ '.tab-selected', Settings.dialog
|
||||||
|
$.rmClass selected, 'tab-selected'
|
||||||
|
$.addClass $(".tab-#{@hyphenatedTitle}", Settings.dialog), 'tab-selected'
|
||||||
section = $ 'section', Settings.dialog
|
section = $ 'section', Settings.dialog
|
||||||
section.innerHTML = null
|
section.innerHTML = null
|
||||||
section.className = "section-#{@title.toLowerCase().replace /\s+/g, '-'}"
|
section.className = "section-#{@hyphenatedTitle}"
|
||||||
@open section, g
|
@open section, g
|
||||||
section.scrollTop = 0
|
section.scrollTop = 0
|
||||||
|
|
||||||
@ -283,7 +290,7 @@ Settings =
|
|||||||
description = arr[1]
|
description = arr[1]
|
||||||
div = $.el 'div',
|
div = $.el 'div',
|
||||||
innerHTML: "<label><input type=checkbox name=\"#{key}\" #{checked}>#{key}</label><span class=description>: #{description}</span>"
|
innerHTML: "<label><input type=checkbox name=\"#{key}\" #{checked}>#{key}</label><span class=description>: #{description}</span>"
|
||||||
$.on $('input', div), 'click', $.cb.checked
|
$.on $('input', div), 'change', $.cb.checked
|
||||||
$.add fs, div
|
$.add fs, div
|
||||||
$.add section, fs
|
$.add section, fs
|
||||||
|
|
||||||
@ -311,7 +318,7 @@ Settings =
|
|||||||
className: 'warning'
|
className: 'warning'
|
||||||
textContent: 'Save me!'
|
textContent: 'Save me!'
|
||||||
download: "<%= meta.name %> v#{g.VERSION}-#{now}.json"
|
download: "<%= meta.name %> v#{g.VERSION}-#{now}.json"
|
||||||
href: "data:application/json;base64,#{btoa unescape encodeURIComponent JSON.stringify data}"
|
href: "data:application/json;base64,#{btoa unescape encodeURIComponent JSON.stringify data, null, 2}"
|
||||||
target: '_blank'
|
target: '_blank'
|
||||||
if $.engine isnt 'gecko'
|
if $.engine isnt 'gecko'
|
||||||
a.click()
|
a.click()
|
||||||
@ -451,7 +458,7 @@ Settings =
|
|||||||
$.add div, ta
|
$.add div, ta
|
||||||
return
|
return
|
||||||
div.innerHTML = """
|
div.innerHTML = """
|
||||||
<div class=warning #{if Conf['Sauce'] then 'hidden' else ''}><code>Filter</code> is disabled.</div>
|
<div class=warning #{if Conf['Filter'] then 'hidden' else ''}><code>Filter</code> is disabled.</div>
|
||||||
<p>
|
<p>
|
||||||
Use <a href=https://developer.mozilla.org/en/JavaScript/Guide/Regular_Expressions>regular expressions</a>, one per line.<br>
|
Use <a href=https://developer.mozilla.org/en/JavaScript/Guide/Regular_Expressions>regular expressions</a>, one per line.<br>
|
||||||
Lines starting with a <code>#</code> will be ignored.<br>
|
Lines starting with a <code>#</code> will be ignored.<br>
|
||||||
@ -509,6 +516,9 @@ Settings =
|
|||||||
<div>Title link: <code>board-title</code></div>
|
<div>Title link: <code>board-title</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>Catalog-only link: <code>board-catalog</code></div>
|
||||||
|
<div>Combinations are possible: <code>board-index-text:"VIP Index"</code></div>
|
||||||
<div>Full board list toggle: <code>toggle-all</code></div>
|
<div>Full board list toggle: <code>toggle-all</code></div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
@ -551,9 +561,9 @@ Settings =
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Custom CSS <span class=warning #{if Conf['Custom CSS'] then 'hidden' else ''}>is disabled.</span></legend>
|
<legend><input type=checkbox name='Custom CSS' #{if Conf['Custom CSS'] then 'checked' else ''}> Custom CSS</legend>
|
||||||
<button id=apply-css>Apply CSS</button>
|
<button id=apply-css>Apply CSS</button>
|
||||||
<textarea name=usercss class=field spellcheck=false></textarea>
|
<textarea name=usercss class=field spellcheck=false #{if Conf['Custom CSS'] then '' else 'disabled'}></textarea>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
"""
|
"""
|
||||||
for name in ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss']
|
for name in ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss']
|
||||||
@ -567,6 +577,7 @@ Settings =
|
|||||||
unless 'usercss' is name
|
unless 'usercss' is name
|
||||||
$.on input, event, Settings[name]
|
$.on input, event, Settings[name]
|
||||||
Settings[name].call input
|
Settings[name].call input
|
||||||
|
$.on $('input[name="Custom CSS"]', section), 'change', Settings.togglecss
|
||||||
$.on $.id('apply-css'), 'click', Settings.usercss
|
$.on $.id('apply-css'), 'click', Settings.usercss
|
||||||
boardnav: ->
|
boardnav: ->
|
||||||
Header.generateBoardList @value
|
Header.generateBoardList @value
|
||||||
@ -591,12 +602,20 @@ Settings =
|
|||||||
favicon: ->
|
favicon: ->
|
||||||
Favicon.switch()
|
Favicon.switch()
|
||||||
Unread.update() if g.VIEW is 'thread' and Conf['Unread Tab Icon']
|
Unread.update() if g.VIEW is 'thread' and Conf['Unread Tab Icon']
|
||||||
@nextElementSibling.innerHTML = "<img src=#{Favicon.unreadSFW}> <img src=#{Favicon.unreadNSFW}> <img src=#{Favicon.unreadDead}>"
|
@nextElementSibling.innerHTML = """
|
||||||
usercss: ->
|
<img src=#{Favicon.default}>
|
||||||
if Conf['Custom CSS']
|
<img src=#{Favicon.unreadSFW}>
|
||||||
CustomCSS.update()
|
<img src=#{Favicon.unreadNSFW}>
|
||||||
else
|
<img src=#{Favicon.unreadDead}>
|
||||||
|
"""
|
||||||
|
togglecss: ->
|
||||||
|
if $('textarea', @parentNode.parentNode).disabled = !@checked
|
||||||
CustomCSS.rmStyle()
|
CustomCSS.rmStyle()
|
||||||
|
else
|
||||||
|
CustomCSS.addStyle()
|
||||||
|
$.cb.checked.call @
|
||||||
|
usercss: ->
|
||||||
|
CustomCSS.update()
|
||||||
|
|
||||||
keybinds: (section) ->
|
keybinds: (section) ->
|
||||||
section.innerHTML = """
|
section.innerHTML = """
|
||||||
@ -632,33 +651,40 @@ Fourchan =
|
|||||||
|
|
||||||
board = g.BOARD.ID
|
board = g.BOARD.ID
|
||||||
if board is 'g'
|
if board is 'g'
|
||||||
|
$.globalEval """
|
||||||
|
window.addEventListener('prettyprint', function(e) {
|
||||||
|
var pre = e.detail;
|
||||||
|
pre.innerHTML = prettyPrintOne(pre.innerHTML);
|
||||||
|
}, false);
|
||||||
|
"""
|
||||||
Post::callbacks.push
|
Post::callbacks.push
|
||||||
name: 'Parse /g/ code'
|
name: 'Parse /g/ code'
|
||||||
cb: @code
|
cb: @code
|
||||||
if board is 'sci'
|
if board is 'sci'
|
||||||
|
# https://github.com/MayhemYDG/4chan-x/issues/645#issuecomment-13704562
|
||||||
|
$.globalEval """
|
||||||
|
window.addEventListener('jsmath', function(e) {
|
||||||
|
if (jsMath.loaded) {
|
||||||
|
// process one post
|
||||||
|
jsMath.ProcessBeforeShowing(e.detail);
|
||||||
|
} else {
|
||||||
|
// load jsMath and process whole document
|
||||||
|
jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);
|
||||||
|
jsMath.Autoload.LoadJsMath();
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
"""
|
||||||
Post::callbacks.push
|
Post::callbacks.push
|
||||||
name: 'Parse /sci/ math'
|
name: 'Parse /sci/ math'
|
||||||
cb: @math
|
cb: @math
|
||||||
code: ->
|
code: ->
|
||||||
return if @isClone
|
return if @isClone
|
||||||
for pre in $$ '.prettyprint', @nodes.comment
|
for pre in $$ '.prettyprint', @nodes.comment
|
||||||
pre.innerHTML = $.unsafeWindow.prettyPrintOne pre.innerHTML
|
$.event 'prettyprint', pre, window
|
||||||
return
|
return
|
||||||
math: ->
|
math: ->
|
||||||
return if @isClone or !$ '.math', @nodes.comment
|
return if @isClone or !$ '.math', @nodes.comment
|
||||||
# https://github.com/MayhemYDG/4chan-x/issues/645#issuecomment-13704562
|
$.event 'jsmath', @nodes.post, window
|
||||||
{jsMath} = $.unsafeWindow
|
|
||||||
if jsMath
|
|
||||||
if jsMath.loaded
|
|
||||||
# process one post
|
|
||||||
jsMath.ProcessBeforeShowing @nodes.post
|
|
||||||
else
|
|
||||||
# load jsMath and process whole document
|
|
||||||
# Yes this requires to be globalEval'd, don't ask me why.
|
|
||||||
$.globalEval """
|
|
||||||
jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);
|
|
||||||
jsMath.Autoload.LoadJsMath();
|
|
||||||
"""
|
|
||||||
parseThread: (threadID, offset, limit) ->
|
parseThread: (threadID, offset, limit) ->
|
||||||
# Fix /sci/
|
# Fix /sci/
|
||||||
# Fix /g/
|
# Fix /g/
|
||||||
@ -959,7 +985,7 @@ Filter =
|
|||||||
|
|
||||||
ThreadHiding =
|
ThreadHiding =
|
||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW isnt 'index' or !Conf['Thread Hiding']
|
return if g.VIEW isnt 'index' or !Conf['Thread Hiding'] and !Conf['Thread Hiding Link']
|
||||||
|
|
||||||
Misc.clearThreads "hiddenThreads.#{g.BOARD}"
|
Misc.clearThreads "hiddenThreads.#{g.BOARD}"
|
||||||
@getHiddenThreads()
|
@getHiddenThreads()
|
||||||
@ -971,7 +997,7 @@ ThreadHiding =
|
|||||||
node: ->
|
node: ->
|
||||||
if data = ThreadHiding.hiddenThreads.threads[@]
|
if data = ThreadHiding.hiddenThreads.threads[@]
|
||||||
ThreadHiding.hide @, data.makeStub
|
ThreadHiding.hide @, data.makeStub
|
||||||
return unless Conf['Hiding Buttons']
|
return unless Conf['Thread Hiding']
|
||||||
$.prepend @OP.nodes.root, ThreadHiding.makeButton @, 'hide'
|
$.prepend @OP.nodes.root, ThreadHiding.makeButton @, 'hide'
|
||||||
|
|
||||||
getHiddenThreads: ->
|
getHiddenThreads: ->
|
||||||
@ -999,7 +1025,7 @@ ThreadHiding =
|
|||||||
|
|
||||||
menu:
|
menu:
|
||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW isnt 'index' or !Conf['Menu'] or !Conf['Thread Hiding']
|
return if g.VIEW isnt 'index' or !Conf['Menu'] or !Conf['Thread Hiding Link']
|
||||||
|
|
||||||
div = $.el 'div',
|
div = $.el 'div',
|
||||||
className: 'hide-thread-link'
|
className: 'hide-thread-link'
|
||||||
@ -1101,7 +1127,7 @@ ThreadHiding =
|
|||||||
|
|
||||||
ReplyHiding =
|
ReplyHiding =
|
||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW is 'catalog' or !Conf['Reply Hiding']
|
return if g.VIEW is 'catalog' or !Conf['Reply Hiding'] and !Conf['Reply Hiding Link']
|
||||||
|
|
||||||
Misc.clearThreads "hiddenPosts.#{g.BOARD}"
|
Misc.clearThreads "hiddenPosts.#{g.BOARD}"
|
||||||
@getHiddenPosts()
|
@getHiddenPosts()
|
||||||
@ -1118,7 +1144,7 @@ ReplyHiding =
|
|||||||
else
|
else
|
||||||
Recursive.apply ReplyHiding.hide, @, data.makeStub, true
|
Recursive.apply ReplyHiding.hide, @, data.makeStub, true
|
||||||
Recursive.add ReplyHiding.hide, @, data.makeStub, true
|
Recursive.add ReplyHiding.hide, @, data.makeStub, true
|
||||||
return unless Conf['Hiding Buttons']
|
return unless Conf['Reply Hiding']
|
||||||
$.replace $('.sideArrows', @nodes.root), ReplyHiding.makeButton @, 'hide'
|
$.replace $('.sideArrows', @nodes.root), ReplyHiding.makeButton @, 'hide'
|
||||||
|
|
||||||
getHiddenPosts: ->
|
getHiddenPosts: ->
|
||||||
@ -1126,7 +1152,7 @@ ReplyHiding =
|
|||||||
|
|
||||||
menu:
|
menu:
|
||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW is 'catalog' or !Conf['Menu'] or !Conf['Reply Hiding']
|
return if g.VIEW is 'catalog' or !Conf['Menu'] or !Conf['Reply Hiding Link']
|
||||||
|
|
||||||
# Hide
|
# Hide
|
||||||
div = $.el 'div',
|
div = $.el 'div',
|
||||||
@ -1336,7 +1362,7 @@ Recursive =
|
|||||||
|
|
||||||
QuoteStrikeThrough =
|
QuoteStrikeThrough =
|
||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW is 'catalog' or !Conf['Reply Hiding'] and !Conf['Filter']
|
return if g.VIEW is 'catalog' or !Conf['Reply Hiding'] and !Conf['Reply Hiding Link'] and !Conf['Filter']
|
||||||
|
|
||||||
Post::callbacks.push
|
Post::callbacks.push
|
||||||
name: 'Strike-through Quotes'
|
name: 'Strike-through Quotes'
|
||||||
@ -1624,7 +1650,7 @@ Keybinds =
|
|||||||
when Conf['Open settings']
|
when Conf['Open settings']
|
||||||
Settings.open()
|
Settings.open()
|
||||||
when Conf['Close']
|
when Conf['Close']
|
||||||
if $.id 'settings'
|
if $.id 'fourchanx-settings'
|
||||||
Settings.close()
|
Settings.close()
|
||||||
else if (notifications = $$ '.notification').length
|
else if (notifications = $$ '.notification').length
|
||||||
for notification in notifications
|
for notification in notifications
|
||||||
@ -1791,7 +1817,6 @@ Keybinds =
|
|||||||
|
|
||||||
focus: (post) ->
|
focus: (post) ->
|
||||||
$.addClass post, 'highlight'
|
$.addClass post, 'highlight'
|
||||||
$('a[title="Highlight this post"]', post).focus()
|
|
||||||
|
|
||||||
Nav =
|
Nav =
|
||||||
init: ->
|
init: ->
|
||||||
@ -2419,10 +2444,10 @@ Misc = # super semantic
|
|||||||
|
|
||||||
return if data.lastChecked > Date.now() - 12 * $.HOUR
|
return if data.lastChecked > Date.now() - 12 * $.HOUR
|
||||||
|
|
||||||
$.ajax "//api.4chan.org/#{g.BOARD}/catalog.json", onload: ->
|
$.ajax "//api.4chan.org/#{g.BOARD}/threads.json", onload: ->
|
||||||
threads = {}
|
threads = {}
|
||||||
for obj in JSON.parse @response
|
for page in JSON.parse @response
|
||||||
for thread in obj.threads
|
for thread in page.threads
|
||||||
if thread.no of data.threads
|
if thread.no of data.threads
|
||||||
threads[thread.no] = data.threads[thread.no]
|
threads[thread.no] = data.threads[thread.no]
|
||||||
unless Object.keys(threads).length
|
unless Object.keys(threads).length
|
||||||
@ -2630,7 +2655,7 @@ QuotePreview =
|
|||||||
# Remove the clone that's in the qp from the array.
|
# Remove the clone that's in the qp from the array.
|
||||||
posts.pop()
|
posts.pop()
|
||||||
for post in posts
|
for post in posts
|
||||||
$.addClass post.nodes.post, 'qphl'
|
$.addClass post.nodes.root, 'qphl'
|
||||||
|
|
||||||
quoterID = $.x('ancestor::*[@id][1]', @).id.match(/\d+$/)[0]
|
quoterID = $.x('ancestor::*[@id][1]', @).id.match(/\d+$/)[0]
|
||||||
clone = Get.postFromRoot qp.firstChild
|
clone = Get.postFromRoot qp.firstChild
|
||||||
@ -2651,7 +2676,7 @@ QuotePreview =
|
|||||||
|
|
||||||
return unless Conf['Quote Highlighting']
|
return unless Conf['Quote Highlighting']
|
||||||
for post in [post].concat post.clones
|
for post in [post].concat post.clones
|
||||||
$.rmClass post.nodes.post, 'qphl'
|
$.rmClass post.nodes.root, 'qphl'
|
||||||
return
|
return
|
||||||
|
|
||||||
QuoteBacklink =
|
QuoteBacklink =
|
||||||
@ -3055,14 +3080,15 @@ Sauce =
|
|||||||
createSauceLink: (link) ->
|
createSauceLink: (link) ->
|
||||||
link = link.replace /%(T?URL|MD5|board)/ig, (parameter) ->
|
link = link.replace /%(T?URL|MD5|board)/ig, (parameter) ->
|
||||||
switch parameter
|
switch parameter
|
||||||
when '%TURL', '%turl'
|
|
||||||
"' + post.file.thumbURL + '"
|
when '%TURL'
|
||||||
when '%URL', '%url'
|
"' + encodeURIComponent(post.file.thumbURL) + '"
|
||||||
"' + post.file.URL + '"
|
when '%URL'
|
||||||
when '%MD5', '%md5'
|
"' + encodeURIComponent(post.file.URL) + '"
|
||||||
|
when '%MD5'
|
||||||
"' + encodeURIComponent(post.file.MD5) + '"
|
"' + encodeURIComponent(post.file.MD5) + '"
|
||||||
when '%board'
|
when '%board'
|
||||||
"' + post.board + '"
|
"' + encodeURIComponent(post.board) + '"
|
||||||
else
|
else
|
||||||
parameter
|
parameter
|
||||||
text = if m = link.match(/;text:(.+)$/) then m[1] else link.match(/(\w+)\.\w+\//)[1]
|
text = if m = link.match(/;text:(.+)$/) then m[1] else link.match(/(\w+)\.\w+\//)[1]
|
||||||
@ -3171,7 +3197,7 @@ ImageExpand =
|
|||||||
# Scroll back to the thumbnail when contracting the image
|
# Scroll back to the thumbnail when contracting the image
|
||||||
# to avoid being left miles away from the relevant post.
|
# to avoid being left miles away from the relevant post.
|
||||||
postRect = post.nodes.root.getBoundingClientRect()
|
postRect = post.nodes.root.getBoundingClientRect()
|
||||||
headRect = Header.bar.getBoundingClientRect()
|
headRect = Header.toggle.getBoundingClientRect()
|
||||||
top = postRect.top - headRect.top - headRect.height - 2
|
top = postRect.top - headRect.top - headRect.height - 2
|
||||||
root = if $.engine is 'webkit'
|
root = if $.engine is 'webkit'
|
||||||
d.body
|
d.body
|
||||||
@ -3222,7 +3248,10 @@ ImageExpand =
|
|||||||
post = Get.postFromNode @
|
post = Get.postFromNode @
|
||||||
$.rm @
|
$.rm @
|
||||||
delete post.file.fullImage
|
delete post.file.fullImage
|
||||||
unless $.hasClass post.file.thumb, 'expanding'
|
# Images can error:
|
||||||
|
# - before the image started loading.
|
||||||
|
# - after the image started loading.
|
||||||
|
unless $.hasClass(post.file.thumb, 'expanding') or $.hasClass post.nodes.root, 'expanded-image'
|
||||||
# Don't try to re-expend if it was already contracted.
|
# Don't try to re-expend if it was already contracted.
|
||||||
return
|
return
|
||||||
ImageExpand.contract post
|
ImageExpand.contract post
|
||||||
@ -3445,7 +3474,7 @@ ExpandThread =
|
|||||||
#goddamit moot
|
#goddamit moot
|
||||||
num = if thread.isSticky
|
num = if thread.isSticky
|
||||||
1
|
1
|
||||||
else switch g.BOARD
|
else switch g.BOARD.ID
|
||||||
# XXX boards config
|
# XXX boards config
|
||||||
when 'b', 'vg', 'q' then 3
|
when 'b', 'vg', 'q' then 3
|
||||||
when 't' then 1
|
when 't' then 1
|
||||||
@ -3493,7 +3522,7 @@ ExpandThread =
|
|||||||
|
|
||||||
# Enable 4chan features.
|
# Enable 4chan features.
|
||||||
if Conf['Enable 4chan\'s Extension']
|
if Conf['Enable 4chan\'s Extension']
|
||||||
$.unsafeWindow.Parser.parseThread thread.ID, 1, nodes.length
|
$.globalEval "Parser.parseThread(#{thread.ID}, 1, #{nodes.length})"
|
||||||
else
|
else
|
||||||
Fourchan.parseThread thread.ID, 1, nodes.length
|
Fourchan.parseThread thread.ID, 1, nodes.length
|
||||||
|
|
||||||
@ -3505,7 +3534,10 @@ ThreadExcerpt =
|
|||||||
name: 'Thread Excerpt'
|
name: 'Thread Excerpt'
|
||||||
cb: @node
|
cb: @node
|
||||||
node: ->
|
node: ->
|
||||||
d.title = Get.threadExcerpt @
|
d.title = if (excerpt = Get.threadExcerpt @).length > 80
|
||||||
|
"#{excerpt[...77]}..."
|
||||||
|
else
|
||||||
|
excerpt
|
||||||
|
|
||||||
Unread =
|
Unread =
|
||||||
init: ->
|
init: ->
|
||||||
@ -3523,6 +3555,7 @@ Unread =
|
|||||||
Unread.lastReadPost = $.get("lastReadPosts.#{@board}", threads: {}).threads[@] or 0
|
Unread.lastReadPost = $.get("lastReadPosts.#{@board}", threads: {}).threads[@] or 0
|
||||||
Unread.posts = []
|
Unread.posts = []
|
||||||
Unread.postsQuotingYou = []
|
Unread.postsQuotingYou = []
|
||||||
|
Unread.titleEl = $ 'title', d.head
|
||||||
Unread.title = d.title
|
Unread.title = d.title
|
||||||
posts = []
|
posts = []
|
||||||
for ID, post of @posts
|
for ID, post of @posts
|
||||||
@ -3604,10 +3637,17 @@ Unread =
|
|||||||
count = Unread.posts.length
|
count = Unread.posts.length
|
||||||
|
|
||||||
if Conf['Unread Count']
|
if Conf['Unread Count']
|
||||||
d.title = if g.DEAD
|
prefix = if count
|
||||||
"(#{Unread.posts.length}) /#{g.BOARD}/ - 404"
|
"(#{count})"
|
||||||
else
|
else
|
||||||
"(#{Unread.posts.length}) #{Unread.title}"
|
''
|
||||||
|
# XXX Chrome bug where it doesn't always update the tab title.
|
||||||
|
# crbug.com/16650
|
||||||
|
# crbug.com/124381
|
||||||
|
Unread.titleEl.textContent = if g.DEAD
|
||||||
|
"#{prefix} /#{g.BOARD}/ - 404"
|
||||||
|
else
|
||||||
|
"#{prefix} #{Unread.title}"
|
||||||
|
|
||||||
return unless Conf['Unread Tab Icon']
|
return unless Conf['Unread Tab Icon']
|
||||||
|
|
||||||
@ -3997,9 +4037,9 @@ ThreadUpdater =
|
|||||||
$.queueTask ->
|
$.queueTask ->
|
||||||
# Enable 4chan features.
|
# Enable 4chan features.
|
||||||
threadID = ThreadUpdater.thread.ID
|
threadID = ThreadUpdater.thread.ID
|
||||||
{length} = ThreadUpdater.root.children
|
{length} = $$ '.thread > .postContainer', ThreadUpdater.root
|
||||||
if Conf['Enable 4chan\'s Extension']
|
if Conf['Enable 4chan\'s Extension']
|
||||||
$.unsafeWindow.Parser.parseThread threadID, -count
|
$.globalEval "Parser.parseThread(#{threadID}, #{-count})"
|
||||||
else
|
else
|
||||||
Fourchan.parseThread threadID, length - count, length
|
Fourchan.parseThread threadID, length - count, length
|
||||||
|
|
||||||
@ -4038,6 +4078,7 @@ ThreadWatcher =
|
|||||||
$.delete 'AutoWatch'
|
$.delete 'AutoWatch'
|
||||||
|
|
||||||
ready: ->
|
ready: ->
|
||||||
|
return unless Main.isThisPageLegit()
|
||||||
ThreadWatcher.refresh()
|
ThreadWatcher.refresh()
|
||||||
$.add d.body, ThreadWatcher.dialog
|
$.add d.body, ThreadWatcher.dialog
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
|
<% if (type === 'userjs') { %>
|
||||||
# Opera doesn't support the @match metadata key,
|
# Opera doesn't support the @match metadata key,
|
||||||
# return 4chan X here if we're not on 4chan.
|
# return 4chan X here if we're not on 4chan.
|
||||||
return unless /^(boards|images|sys)\.4chan\.org$/.test location.hostname
|
return unless /^(boards|images|sys)\.4chan\.org$/.test location.hostname
|
||||||
|
<% } %>
|
||||||
|
|
||||||
Conf = {}
|
Conf = {}
|
||||||
c = console
|
c = console
|
||||||
d = document
|
d = document
|
||||||
doc = null
|
doc = d.documentElement
|
||||||
g =
|
g =
|
||||||
VERSION: '<%= version %>'
|
VERSION: '<%= version %>'
|
||||||
NAMESPACE: '<%= meta.name %>.'
|
NAMESPACE: '<%= meta.name %>.'
|
||||||
boards: {}
|
boards: {}
|
||||||
|
|||||||
@ -41,28 +41,28 @@ class Post
|
|||||||
backlinks: info.getElementsByClassName 'backlink'
|
backlinks: info.getElementsByClassName 'backlink'
|
||||||
|
|
||||||
@info = {}
|
@info = {}
|
||||||
if subject = $ '.subject', info
|
if subject = $ '.subject', info
|
||||||
@nodes.subject = subject
|
@nodes.subject = subject
|
||||||
@info.subject = subject.textContent
|
@info.subject = subject.textContent
|
||||||
if name = $ '.name', info
|
if name = $ '.name', info
|
||||||
@nodes.name = name
|
@nodes.name = name
|
||||||
@info.name = name.textContent
|
@info.name = name.textContent
|
||||||
if email = $ '.useremail', info
|
if email = $ '.useremail', info
|
||||||
@nodes.email = email
|
@nodes.email = email
|
||||||
@info.email = decodeURIComponent email.href[7..]
|
@info.email = decodeURIComponent email.href[7..]
|
||||||
if tripcode = $ '.postertrip', info
|
if tripcode = $ '.postertrip', info
|
||||||
@nodes.tripcode = tripcode
|
@nodes.tripcode = tripcode
|
||||||
@info.tripcode = tripcode.textContent
|
@info.tripcode = tripcode.textContent
|
||||||
if uniqueID = $ '.posteruid', info
|
if uniqueID = $ '.posteruid', info
|
||||||
@nodes.uniqueID = uniqueID
|
@nodes.uniqueID = uniqueID
|
||||||
@info.uniqueID = uniqueID.firstElementChild.textContent
|
@info.uniqueID = uniqueID.firstElementChild.textContent
|
||||||
if capcode = $ '.capcode', info
|
if capcode = $ '.capcode.hand', info
|
||||||
@nodes.capcode = capcode
|
@nodes.capcode = capcode
|
||||||
@info.capcode = capcode.textContent
|
@info.capcode = capcode.textContent.replace '## ', ''
|
||||||
if flag = $ '.countryFlag', info
|
if flag = $ '.countryFlag', info
|
||||||
@nodes.flag = flag
|
@nodes.flag = flag
|
||||||
@info.flag = flag.title
|
@info.flag = flag.title
|
||||||
if date = $ '.dateTime', info
|
if date = $ '.dateTime', info
|
||||||
@nodes.date = date
|
@nodes.date = date
|
||||||
@info.date = new Date date.dataset.utc * 1000
|
@info.date = new Date date.dataset.utc * 1000
|
||||||
if Conf['Quick Reply']
|
if Conf['Quick Reply']
|
||||||
@ -284,9 +284,6 @@ class Clone extends Post
|
|||||||
|
|
||||||
Main =
|
Main =
|
||||||
init: ->
|
init: ->
|
||||||
$.asap (-> d.documentElement), ->
|
|
||||||
doc = d.documentElement
|
|
||||||
|
|
||||||
# flatten Config into Conf
|
# flatten Config into Conf
|
||||||
# and get saved or default values
|
# and get saved or default values
|
||||||
flatten = (parent, obj) ->
|
flatten = (parent, obj) ->
|
||||||
@ -486,7 +483,9 @@ Main =
|
|||||||
Main.handleErrors errors if errors
|
Main.handleErrors errors if errors
|
||||||
|
|
||||||
addCallback: (e) ->
|
addCallback: (e) ->
|
||||||
obj = e.detail
|
obj = e.detail
|
||||||
|
unless typeof obj.callback.name is 'string'
|
||||||
|
throw new Error "Invalid callback name: #{obj.callback.name}"
|
||||||
Klass = if obj.type is 'Post'
|
Klass = if obj.type is 'Post'
|
||||||
Post
|
Post
|
||||||
else
|
else
|
||||||
@ -504,7 +503,7 @@ Main =
|
|||||||
return
|
return
|
||||||
|
|
||||||
div = $.el 'div',
|
div = $.el 'div',
|
||||||
innerHTML: "#{errors.length} errors occured. [<a href=javascript:;>show</a>]"
|
innerHTML: "#{errors.length} errors occurred. [<a href=javascript:;>show</a>]"
|
||||||
$.on div.lastElementChild, 'click', ->
|
$.on div.lastElementChild, 'click', ->
|
||||||
if @textContent is 'show'
|
if @textContent is 'show'
|
||||||
@textContent = 'hide'
|
@textContent = 'hide'
|
||||||
@ -533,7 +532,9 @@ Main =
|
|||||||
isThisPageLegit: ->
|
isThisPageLegit: ->
|
||||||
# 404 error page or similar.
|
# 404 error page or similar.
|
||||||
unless 'thisPageIsLegit' of Main
|
unless 'thisPageIsLegit' of Main
|
||||||
Main.thisPageIsLegit = !$('link[href*="favicon-status.ico"]', d.head) and d.title isnt '4chan - Temporarily Offline'
|
Main.thisPageIsLegit = location.hostname is 'boards.4chan.org' and
|
||||||
|
!$('link[href*="favicon-status.ico"]', d.head) and
|
||||||
|
d.title not in ['4chan - Temporarily Offline', '4chan - Error']
|
||||||
Main.thisPageIsLegit
|
Main.thisPageIsLegit
|
||||||
|
|
||||||
css: """
|
css: """
|
||||||
|
|||||||
@ -3,6 +3,11 @@
|
|||||||
"version": "<%= version %>",
|
"version": "<%= version %>",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"description": "<%= description %>",
|
"description": "<%= description %>",
|
||||||
|
"icons": {
|
||||||
|
"16": "icon16.png",
|
||||||
|
"48": "icon48.png",
|
||||||
|
"128": "icon128.png"
|
||||||
|
},
|
||||||
"content_scripts": [{
|
"content_scripts": [{
|
||||||
"js": ["script.js"],
|
"js": ["script.js"],
|
||||||
"matches": <%= JSON.stringify(meta.matches) %>,
|
"matches": <%= JSON.stringify(meta.matches) %>,
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
// @grant GM_deleteValue
|
// @grant GM_deleteValue
|
||||||
// @grant GM_openInTab
|
// @grant GM_openInTab
|
||||||
// @run-at document-start
|
// @run-at document-start
|
||||||
// @updateURL <%= meta.repo %>raw/<%= meta.mainBranch %>/<%= name %>.meta.js
|
// @updateURL <%= meta.page %>builds/<%= name %>.meta.js
|
||||||
// @downloadURL <%= meta.repo %>raw/<%= meta.mainBranch %>/<%= name %>.user.js
|
// @downloadURL <%= meta.page %>builds/<%= name %>.user.js
|
||||||
// @icon data:image/gif;base64,<%= grunt.file.read('img/icon.gif', {encoding: 'base64'}) %>
|
// @icon data:image/png;base64,<%= grunt.file.read('img/icon48.png', {encoding: 'base64'}) %>
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
|||||||
@ -486,6 +486,7 @@ QR =
|
|||||||
# so we generate thumbnails `s` times bigger then expected
|
# so we generate thumbnails `s` times bigger then expected
|
||||||
# to avoid crappy resized quality.
|
# to avoid crappy resized quality.
|
||||||
s = 90*2
|
s = 90*2
|
||||||
|
s *= 3 if @file.type is 'image/gif' # let them animate
|
||||||
{height, width} = img
|
{height, width} = img
|
||||||
if height < s or width < s
|
if height < s or width < s
|
||||||
@URL = fileURL if window.URL
|
@URL = fileURL if window.URL
|
||||||
@ -581,6 +582,12 @@ QR =
|
|||||||
return unless @isEnabled = !!$.id 'captchaFormPart'
|
return unless @isEnabled = !!$.id 'captchaFormPart'
|
||||||
$.asap (-> $.id 'recaptcha_challenge_field_holder'), @ready.bind @
|
$.asap (-> $.id 'recaptcha_challenge_field_holder'), @ready.bind @
|
||||||
ready: ->
|
ready: ->
|
||||||
|
setLifetime = (e) => @lifetime = e.detail
|
||||||
|
$.on window, 'captcha:timeout', setLifetime
|
||||||
|
$.globalEval 'window.dispatchEvent(new CustomEvent("captcha:timeout", {detail: RecaptchaState.timeout}))'
|
||||||
|
$.off window, 'captcha:timeout', setLifetime
|
||||||
|
c.log @lifetime
|
||||||
|
|
||||||
imgContainer = $.el 'div',
|
imgContainer = $.el 'div',
|
||||||
className: 'captcha-img'
|
className: 'captcha-img'
|
||||||
title: 'Reload'
|
title: 'Reload'
|
||||||
@ -648,7 +655,7 @@ QR =
|
|||||||
load: ->
|
load: ->
|
||||||
return unless @nodes.challenge.firstChild
|
return unless @nodes.challenge.firstChild
|
||||||
# -1 minute to give upload some time.
|
# -1 minute to give upload some time.
|
||||||
@timeout = Date.now() + $.unsafeWindow.RecaptchaState.timeout * $.SECOND - $.MINUTE
|
@timeout = Date.now() + @lifetime * $.SECOND - $.MINUTE
|
||||||
challenge = @nodes.challenge.firstChild.value
|
challenge = @nodes.challenge.firstChild.value
|
||||||
@nodes.img.alt = challenge
|
@nodes.img.alt = challenge
|
||||||
@nodes.img.src = "//www.google.com/recaptcha/api/image?c=#{challenge}"
|
@nodes.img.src = "//www.google.com/recaptcha/api/image?c=#{challenge}"
|
||||||
@ -666,7 +673,7 @@ QR =
|
|||||||
@nodes.input.alt = count # For XTRM RICE.
|
@nodes.input.alt = count # For XTRM RICE.
|
||||||
reload: (focus) ->
|
reload: (focus) ->
|
||||||
# the 't' argument prevents the input from being focused
|
# the 't' argument prevents the input from being focused
|
||||||
$.unsafeWindow.Recaptcha.reload 't'
|
$.globalEval 'Recaptcha.reload("t")'
|
||||||
# Focus if we meant to.
|
# Focus if we meant to.
|
||||||
@nodes.input.focus() if focus
|
@nodes.input.focus() if focus
|
||||||
keydown: (e) ->
|
keydown: (e) ->
|
||||||
@ -704,14 +711,14 @@ QR =
|
|||||||
<span id=char-count></span>
|
<span id=char-count></span>
|
||||||
</div>
|
</div>
|
||||||
<div id=file-n-submit>
|
<div id=file-n-submit>
|
||||||
|
<input type=submit>
|
||||||
<input id=qr-file-button type=button value='Choose files'>
|
<input id=qr-file-button type=button value='Choose files'>
|
||||||
<span id=qr-filename-container>
|
<span id=qr-filename-container>
|
||||||
<span id=qr-no-file>No selected file</span>
|
<span id=qr-no-file>No selected file</span>
|
||||||
<span id=qr-filename></span>
|
<span id=qr-filename></span>
|
||||||
</span>
|
</span>
|
||||||
<a id=qr-filerm href=javascript:; title='Remove file' tabindex=-1>×</a>
|
<a id=qr-filerm href=javascript:; title='Remove file'>×</a>
|
||||||
<input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=-1>
|
<input type=checkbox id=qr-file-spoiler title='Spoiler image'>
|
||||||
<input type=submit>
|
|
||||||
</div>
|
</div>
|
||||||
<input type=file multiple>
|
<input type=file multiple>
|
||||||
</form>
|
</form>
|
||||||
@ -877,12 +884,14 @@ QR =
|
|||||||
callbacks =
|
callbacks =
|
||||||
onload: QR.response
|
onload: QR.response
|
||||||
onerror: ->
|
onerror: ->
|
||||||
|
# Connection error, or
|
||||||
|
# www.4chan.org/banned
|
||||||
delete QR.req
|
delete QR.req
|
||||||
post.unlock()
|
post.unlock()
|
||||||
QR.cooldown.auto = false
|
QR.cooldown.auto = false
|
||||||
QR.status()
|
QR.status()
|
||||||
# Connection error.
|
QR.error $.el 'span',
|
||||||
QR.error 'Network error.'
|
innerHTML: 'Connection error. You may have been <a href=//www.4chan.org/banned target=_blank>banned</a>.'
|
||||||
opts =
|
opts =
|
||||||
form: $.formData postData
|
form: $.formData postData
|
||||||
upCallbacks:
|
upCallbacks:
|
||||||
@ -966,6 +975,7 @@ QR =
|
|||||||
[_, threadID, postID] = h1.nextSibling.textContent.match /thread:(\d+),no:(\d+)/
|
[_, threadID, postID] = h1.nextSibling.textContent.match /thread:(\d+),no:(\d+)/
|
||||||
postID = +postID
|
postID = +postID
|
||||||
threadID = +threadID or postID
|
threadID = +threadID or postID
|
||||||
|
isReply = threadID isnt postID
|
||||||
|
|
||||||
(QR.yourPosts.threads[threadID] or= []).push postID
|
(QR.yourPosts.threads[threadID] or= []).push postID
|
||||||
$.set "yourPosts.#{g.BOARD}", QR.yourPosts
|
$.set "yourPosts.#{g.BOARD}", QR.yourPosts
|
||||||
@ -980,18 +990,15 @@ QR =
|
|||||||
}, QR.nodes.el
|
}, QR.nodes.el
|
||||||
|
|
||||||
# Enable auto-posting if we have stuff to post, disable it otherwise.
|
# Enable auto-posting if we have stuff to post, disable it otherwise.
|
||||||
QR.cooldown.auto = QR.posts.length > 1
|
QR.cooldown.auto = QR.posts.length > 1 and isReply
|
||||||
|
|
||||||
post.rm()
|
post.rm()
|
||||||
|
|
||||||
QR.cooldown.set
|
QR.cooldown.set {req, post, isReply}
|
||||||
req: req
|
|
||||||
post: post
|
|
||||||
isReply: !!threadID
|
|
||||||
|
|
||||||
if threadID is postID # new thread
|
if threadID is postID # new thread
|
||||||
URL = "/#{g.BOARD}/res/#{threadID}"
|
URL = "/#{g.BOARD}/res/#{threadID}"
|
||||||
else if g.VIEW is 'index' and !QR.cooldown.auto # posting from the index
|
else if g.VIEW is 'index' and !QR.cooldown.auto and Conf['Open Post in New Tab'] # replying from the index
|
||||||
URL = "/#{g.BOARD}/res/#{threadID}#p#{postID}"
|
URL = "/#{g.BOARD}/res/#{threadID}#p#{postID}"
|
||||||
if URL
|
if URL
|
||||||
if Conf['Open Post in New Tab']
|
if Conf['Open Post in New Tab']
|
||||||
|
|||||||
@ -6,7 +6,7 @@ Report =
|
|||||||
form = $ 'form'
|
form = $ 'form'
|
||||||
field = $.id 'recaptcha_response_field'
|
field = $.id 'recaptcha_response_field'
|
||||||
$.on field, 'keydown', (e) ->
|
$.on field, 'keydown', (e) ->
|
||||||
$.unsafeWindow.Recaptcha.reload 't' if e.keyCode is 8 and not field.value
|
$.globalEval 'Recaptcha.reload("t")' if e.keyCode is 8 and not field.value
|
||||||
$.on form, 'submit', (e) ->
|
$.on form, 'submit', (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
response = field.value.trim()
|
response = field.value.trim()
|
||||||
|
|||||||