Merge branch 'v3' of git://github.com/MayhemYDG/4chan-x into v3
Conflicts: CHANGELOG.md Gruntfile.coffee LICENSE css/style.css html/Posting/QR.html img/changelog/3.2.0/0.png package.json src/General/Get.coffee src/General/Header.coffee src/General/Main.coffee src/General/Notice.coffee src/General/Notification.coffee src/General/Settings.coffee src/General/img/icon.gif src/General/lib/notification.class src/Monitoring/Favicon.coffee src/Posting/QuickReply.coffee src/Quotelinks/QuoteOP.coffee
@ -1,3 +1,9 @@
|
|||||||
|
**MayhemYDG**:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- The QR now allows you to edit the filename on the fly:
|
||||||
|
|
||||||
### v1.2.32
|
### v1.2.32
|
||||||
*2013-08-16*
|
*2013-08-16*
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ module.exports = (grunt) ->
|
|||||||
'src/General/Build.coffee'
|
'src/General/Build.coffee'
|
||||||
'src/General/Get.coffee'
|
'src/General/Get.coffee'
|
||||||
'src/General/UI.coffee'
|
'src/General/UI.coffee'
|
||||||
|
'src/General/Notice.coffee'
|
||||||
'src/Filtering/**/*'
|
'src/Filtering/**/*'
|
||||||
'src/Quotelinks/**/*'
|
'src/Quotelinks/**/*'
|
||||||
'src/Linkification/**/*'
|
'src/Linkification/**/*'
|
||||||
@ -133,7 +134,7 @@ module.exports = (grunt) ->
|
|||||||
tmpcrx: 'tmp-crx'
|
tmpcrx: 'tmp-crx'
|
||||||
tmpuserscript: 'tmp-userscript'
|
tmpuserscript: 'tmp-userscript'
|
||||||
|
|
||||||
require('matchdep').filterDev('grunt-*').forEach grunt.loadNpmTasks
|
require('load-grunt-tasks') grunt
|
||||||
|
|
||||||
grunt.registerTask 'default', [
|
grunt.registerTask 'default', [
|
||||||
'build'
|
'build'
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name 4chan X
|
// @name 4chan X
|
||||||
// @version 1.2.32
|
// @version 1.2.32
|
||||||
|
// @minGMVer 1.13
|
||||||
|
// @minFFVer 22
|
||||||
// @namespace 4chan-X
|
// @namespace 4chan-X
|
||||||
// @description Cross-browser userscript for maximum lurking on 4chan.
|
// @description Cross-browser userscript for maximum lurking on 4chan.
|
||||||
// @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
// @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||||
|
|||||||
BIN
img/changelog/3.2.0/0.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
BIN
img/changelog/3.8.0/0.gif
Normal file
|
After Width: | Height: | Size: 690 KiB |
@ -2,7 +2,6 @@
|
|||||||
"name": "4chan-X",
|
"name": "4chan-X",
|
||||||
"version": "1.2.32",
|
"version": "1.2.32",
|
||||||
"description": "Cross-browser userscript for maximum lurking on 4chan.",
|
"description": "Cross-browser userscript for maximum lurking on 4chan.",
|
||||||
|
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "4chan X",
|
"name": "4chan X",
|
||||||
"repo": "https://github.com/seaweedchan/4chan-x/",
|
"repo": "https://github.com/seaweedchan/4chan-x/",
|
||||||
@ -21,6 +20,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"font-awesome": "git://github.com/MayhemYDG/Font-Awesome.git#df4285951124f9ca1f3907438462e5ba9e464bcb",
|
||||||
"grunt": "~0.4.1",
|
"grunt": "~0.4.1",
|
||||||
"grunt-bump": "~0.0.11",
|
"grunt-bump": "~0.0.11",
|
||||||
"grunt-concurrent": "~0.3.0",
|
"grunt-concurrent": "~0.3.0",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
"grunt-contrib-copy": "~0.4.1",
|
"grunt-contrib-copy": "~0.4.1",
|
||||||
"grunt-contrib-watch": "~0.5.0",
|
"grunt-contrib-watch": "~0.5.0",
|
||||||
"grunt-shell": "~0.3.1",
|
"grunt-shell": "~0.3.1",
|
||||||
"matchdep": "~0.1.2"
|
"load-grunt-tasks": "~0.1.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@ -33,7 +33,7 @@ Filter =
|
|||||||
regexp = RegExp regexp[1], regexp[2]
|
regexp = RegExp regexp[1], regexp[2]
|
||||||
catch err
|
catch err
|
||||||
# I warned you, bro.
|
# I warned you, bro.
|
||||||
new Notification 'warning', err.message, 60
|
new Notice 'warning', err.message, 60
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Filter OPs along with their threads, replies only, or both.
|
# Filter OPs along with their threads, replies only, or both.
|
||||||
|
|||||||
@ -156,11 +156,8 @@ Get =
|
|||||||
# https://github.com/eksopl/asagi/blob/master/src/main/java/net/easymodo/asagi/Yotsuba.java#L109-138
|
# https://github.com/eksopl/asagi/blob/master/src/main/java/net/easymodo/asagi/Yotsuba.java#L109-138
|
||||||
bq.innerHTML = bq.innerHTML.replace ///
|
bq.innerHTML = bq.innerHTML.replace ///
|
||||||
\n
|
\n
|
||||||
| \[/?b\]
|
|
|
||||||
| \[/?spoiler\]
|
\[/?[a-z]+(:lit)?\]
|
||||||
| \[/?code\]
|
|
||||||
| \[/?moot\]
|
|
||||||
| \[/?banned\]
|
|
||||||
///g, Get.parseMarkup
|
///g, Get.parseMarkup
|
||||||
|
|
||||||
comment = bq.innerHTML
|
comment = bq.innerHTML
|
||||||
@ -234,6 +231,8 @@ Get =
|
|||||||
when '[/moot]'
|
when '[/moot]'
|
||||||
'</div>'
|
'</div>'
|
||||||
when '[banned]'
|
when '[banned]'
|
||||||
'<b style="color: red;">'
|
'<strong style="color: red;">'
|
||||||
when '[/banned]'
|
when '[/banned]'
|
||||||
'</b>'
|
'</strong>'
|
||||||
|
else
|
||||||
|
text.replace ':lit', ''
|
||||||
|
|||||||
@ -258,8 +258,7 @@ Header =
|
|||||||
'automatically hide itself.'
|
'automatically hide itself.'
|
||||||
else
|
else
|
||||||
'remain visible.'}"
|
'remain visible.'}"
|
||||||
|
new Notice 'info', message, 2
|
||||||
new Notification 'info', message, 2
|
|
||||||
|
|
||||||
setFooterVisibility: (hide) ->
|
setFooterVisibility: (hide) ->
|
||||||
Header.footerToggler.checked = hide
|
Header.footerToggler.checked = hide
|
||||||
@ -321,5 +320,5 @@ Header =
|
|||||||
|
|
||||||
createNotification: (e) ->
|
createNotification: (e) ->
|
||||||
{type, content, lifetime, cb} = e.detail
|
{type, content, lifetime, cb} = e.detail
|
||||||
notif = new Notification type, content, lifetime
|
notif = new Notice type, content, lifetime
|
||||||
cb notif if cb
|
cb notif if cb
|
||||||
|
|||||||
@ -13,7 +13,7 @@ Main =
|
|||||||
Conf[parent] = obj
|
Conf[parent] = obj
|
||||||
return
|
return
|
||||||
flatten null, Config
|
flatten null, Config
|
||||||
for db in DataBoards
|
for db in DataBoard.keys
|
||||||
Conf[db] = boards: {}
|
Conf[db] = boards: {}
|
||||||
Conf['selectedArchives'] = {}
|
Conf['selectedArchives'] = {}
|
||||||
Conf['CachedTitles'] = []
|
Conf['CachedTitles'] = []
|
||||||
@ -21,7 +21,7 @@ Main =
|
|||||||
$.extend Conf, items
|
$.extend Conf, items
|
||||||
<% if (type === 'crx') { %>
|
<% if (type === 'crx') { %>
|
||||||
unless items
|
unless items
|
||||||
new Notification 'error', $.el 'span',
|
new Notice 'error', $.el 'span',
|
||||||
innerHTML: """
|
innerHTML: """
|
||||||
It seems like your <%= meta.name %> settings became corrupted due to a <a href="https://code.google.com/p/chromium/issues/detail?id=261623" target=_blank>Chrome bug</a>.<br>
|
It seems like your <%= meta.name %> settings became corrupted due to a <a href="https://code.google.com/p/chromium/issues/detail?id=261623" target=_blank>Chrome bug</a>.<br>
|
||||||
Unfortunately, you'll have to <a href="https://github.com/MayhemYDG/4chan-x/wiki/FAQ#known-problems" target=_blank>fix it yourself</a>.
|
Unfortunately, you'll have to <a href="https://github.com/MayhemYDG/4chan-x/wiki/FAQ#known-problems" target=_blank>fix it yourself</a>.
|
||||||
@ -236,7 +236,7 @@ Main =
|
|||||||
try
|
try
|
||||||
localStorage.getItem '4chan-settings'
|
localStorage.getItem '4chan-settings'
|
||||||
catch err
|
catch err
|
||||||
new Notification 'warning', 'Cookies need to be enabled on 4chan for <%= meta.name %> to properly function.', 30
|
new Notice 'warning', 'Cookies need to be enabled on 4chan for <%= meta.name %> to properly function.', 30
|
||||||
|
|
||||||
$.event '4chanXInitFinished'
|
$.event '4chanXInitFinished'
|
||||||
|
|
||||||
@ -318,7 +318,7 @@ Main =
|
|||||||
else if errors.length is 1
|
else if errors.length is 1
|
||||||
error = errors[0]
|
error = errors[0]
|
||||||
if error
|
if error
|
||||||
new Notification 'error', Main.parseError(error), 15
|
new Notice 'error', Main.parseError(error), 15
|
||||||
return
|
return
|
||||||
|
|
||||||
div = $.el 'div',
|
div = $.el 'div',
|
||||||
@ -334,7 +334,7 @@ Main =
|
|||||||
for error in errors
|
for error in errors
|
||||||
$.add logs, Main.parseError error
|
$.add logs, Main.parseError error
|
||||||
|
|
||||||
new Notification 'error', [div, logs], 30
|
new Notice 'error', [div, logs], 30
|
||||||
|
|
||||||
parseError: (data) ->
|
parseError: (data) ->
|
||||||
Main.logError data
|
Main.logError data
|
||||||
@ -358,6 +358,13 @@ Main =
|
|||||||
Main.thisPageIsLegit
|
Main.thisPageIsLegit
|
||||||
|
|
||||||
css: """
|
css: """
|
||||||
|
@font-face {
|
||||||
|
font-family: 'FontAwesome';
|
||||||
|
src: url('data:application/font-woff;base64,<%= grunt.file.read('node_modules/font-awesome/font/fontawesome-webfont.woff', {encoding: 'base64'}) %>') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
<%= grunt.file.read('node_modules/font-awesome/css/font-awesome.min.css').replace(/@font-face\{[^}]+\}/, '').replace(/\\/g, '\\\\') %>
|
||||||
<%= grunt.file.read('src/General/css/style.css') %>
|
<%= grunt.file.read('src/General/css/style.css') %>
|
||||||
<%= grunt.file.read('src/General/css/yotsuba.css') %>
|
<%= grunt.file.read('src/General/css/yotsuba.css') %>
|
||||||
<%= grunt.file.read('src/General/css/yotsuba-b.css') %>
|
<%= grunt.file.read('src/General/css/yotsuba-b.css') %>
|
||||||
|
|||||||
29
src/General/Notice.coffee
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
class Notice
|
||||||
|
constructor: (type, content, @timeout) ->
|
||||||
|
@el = $.el 'div',
|
||||||
|
innerHTML: '<a href=javascript:; class=close title=Close>×</a><div class=message></div>'
|
||||||
|
@el.style.opacity = 0
|
||||||
|
@setType type
|
||||||
|
$.on @el.firstElementChild, 'click', @close
|
||||||
|
if typeof content is 'string'
|
||||||
|
content = $.tn content
|
||||||
|
$.add @el.lastElementChild, content
|
||||||
|
|
||||||
|
$.ready @add
|
||||||
|
|
||||||
|
setType: (type) ->
|
||||||
|
@el.className = "notification #{type}"
|
||||||
|
|
||||||
|
add: =>
|
||||||
|
if d.hidden
|
||||||
|
$.on d, 'visibilitychange', @add
|
||||||
|
return
|
||||||
|
$.off d, 'visibilitychange', @add
|
||||||
|
$.add $.id('notifications'), @el
|
||||||
|
@el.clientHeight # force reflow
|
||||||
|
@el.style.opacity = 1
|
||||||
|
setTimeout @close, @timeout * $.SECOND if @timeout
|
||||||
|
|
||||||
|
close: =>
|
||||||
|
$.off d, 'visibilitychange', @add
|
||||||
|
$.rm @el
|
||||||
@ -17,7 +17,7 @@ Settings =
|
|||||||
el = $.el 'span',
|
el = $.el 'span',
|
||||||
innerHTML: "<%= meta.name %> has been updated to <a href='#{changelog}' target=_blank>version #{g.VERSION}</a>."
|
innerHTML: "<%= meta.name %> has been updated to <a href='#{changelog}' target=_blank>version #{g.VERSION}</a>."
|
||||||
if Conf['Show Updated Notifications']
|
if Conf['Show Updated Notifications']
|
||||||
new Notification 'info', el, 30
|
new Notice 'info', el, 30
|
||||||
else
|
else
|
||||||
$.on d, '4chanXInitFinished', Settings.open
|
$.on d, '4chanXInitFinished', Settings.open
|
||||||
$.set 'previousversion', g.VERSION
|
$.set 'previousversion', g.VERSION
|
||||||
@ -151,7 +151,7 @@ Settings =
|
|||||||
data =
|
data =
|
||||||
version: g.VERSION
|
version: g.VERSION
|
||||||
date: now
|
date: now
|
||||||
for db in DataBoards
|
for db in DataBoard.keys
|
||||||
Conf[db] = boards: {}
|
Conf[db] = boards: {}
|
||||||
# Make sure to export the most recent data.
|
# Make sure to export the most recent data.
|
||||||
$.get Conf, (Conf) ->
|
$.get Conf, (Conf) ->
|
||||||
|
|||||||
@ -23,8 +23,7 @@ UI = do ->
|
|||||||
|
|
||||||
constructor: (@type) ->
|
constructor: (@type) ->
|
||||||
# Doc here: https://github.com/MayhemYDG/4chan-x/wiki/Menu-API
|
# Doc here: https://github.com/MayhemYDG/4chan-x/wiki/Menu-API
|
||||||
$.on d, 'AddMenuEntry', @addEntry.bind @
|
$.on d, 'AddMenuEntry', @addEntry
|
||||||
@close = close.bind @
|
|
||||||
@entries = []
|
@entries = []
|
||||||
|
|
||||||
makeMenu: ->
|
makeMenu: ->
|
||||||
@ -33,7 +32,7 @@ UI = do ->
|
|||||||
id: 'menu'
|
id: 'menu'
|
||||||
tabIndex: 0
|
tabIndex: 0
|
||||||
$.on menu, 'click', (e) -> e.stopPropagation()
|
$.on menu, 'click', (e) -> e.stopPropagation()
|
||||||
$.on menu, 'keydown', @keybinds.bind @
|
$.on menu, 'keydown', @keybinds
|
||||||
menu
|
menu
|
||||||
|
|
||||||
toggle: (e, button, data) ->
|
toggle: (e, button, data) ->
|
||||||
@ -114,7 +113,7 @@ UI = do ->
|
|||||||
$.add entry.el, submenu
|
$.add entry.el, submenu
|
||||||
return
|
return
|
||||||
|
|
||||||
close = ->
|
close: =>
|
||||||
$.rm currentMenu
|
$.rm currentMenu
|
||||||
$.rmClass lastToggledButton, 'active'
|
$.rmClass lastToggledButton, 'active'
|
||||||
currentMenu = null
|
currentMenu = null
|
||||||
@ -127,7 +126,7 @@ UI = do ->
|
|||||||
+(first.style.order or first.style.webkitOrder) - +(second.style.order or second.style.webkitOrder)
|
+(first.style.order or first.style.webkitOrder) - +(second.style.order or second.style.webkitOrder)
|
||||||
entries[entries.indexOf(entry) + direction]
|
entries[entries.indexOf(entry) + direction]
|
||||||
|
|
||||||
keybinds: (e) ->
|
keybinds: (e) =>
|
||||||
entry = $ '.focused', currentMenu
|
entry = $ '.focused', currentMenu
|
||||||
while subEntry = $ '.focused', entry
|
while subEntry = $ '.focused', entry
|
||||||
entry = subEntry
|
entry = subEntry
|
||||||
@ -185,7 +184,7 @@ UI = do ->
|
|||||||
style.left = left
|
style.left = left
|
||||||
style.right = right
|
style.right = right
|
||||||
|
|
||||||
addEntry: (e) ->
|
addEntry: (e) =>
|
||||||
entry = e.detail
|
entry = e.detail
|
||||||
return if entry.type isnt @type
|
return if entry.type isnt @type
|
||||||
@parseEntry entry
|
@parseEntry entry
|
||||||
|
|||||||
@ -773,18 +773,24 @@ input.field.tripped:not(:hover):not(:focus) {
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
/* Fake File Input */
|
/* Fake File Input */
|
||||||
|
input#qr-filename {
|
||||||
|
border: none !important;
|
||||||
|
width: 65%;
|
||||||
|
padding: 0px 4px;
|
||||||
|
}
|
||||||
#qr-filename,
|
#qr-filename,
|
||||||
|
#qr-filesize,
|
||||||
.has-file #qr-no-file {
|
.has-file #qr-no-file {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#qr-no-file,
|
#qr-no-file,
|
||||||
.has-file #qr-filename {
|
.has-file #qr-filename,
|
||||||
|
.has-file #qr-filesize {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0px 4px;
|
margin: 0 0 2px;
|
||||||
margin-bottom: 2px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
max-width: 88%;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
#qr-no-file {
|
#qr-no-file {
|
||||||
color: #AAA;
|
color: #AAA;
|
||||||
|
|||||||
@ -10,9 +10,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<form>
|
<form>
|
||||||
<div class=persona>
|
<div class=persona>
|
||||||
<input name=name data-name=name list="list-name" placeholder=Name class=field size=1 tabindex=10>
|
<input data-name=name list="list-name" placeholder=Name class=field size=1 tabindex=10>
|
||||||
<input name=email data-name=email list="list-email" placeholder=E-mail class=field size=1 tabindex=20>
|
<input data-name=email list="list-email" placeholder=E-mail class=field size=1 tabindex=20>
|
||||||
<input name=sub data-name=sub list="list-sub" placeholder=Subject class=field size=1 tabindex=30>
|
<input data-name=sub list="list-sub" placeholder=Subject class=field size=1 tabindex=30>
|
||||||
</div>
|
</div>
|
||||||
<div class=textarea>
|
<div class=textarea>
|
||||||
<textarea data-name=com placeholder=Comment class=field tabindex=40></textarea>
|
<textarea data-name=com placeholder=Comment class=field tabindex=40></textarea>
|
||||||
@ -25,7 +25,8 @@
|
|||||||
<div id=file-n-submit>
|
<div id=file-n-submit>
|
||||||
<span id=qr-filename-container class=field tabindex=60>
|
<span id=qr-filename-container class=field tabindex=60>
|
||||||
<span id=qr-no-file>No selected file</span>
|
<span id=qr-no-file>No selected file</span>
|
||||||
<span id=qr-filename></span>
|
<input id="qr-filename" data-name="filename" spellcheck="false">
|
||||||
|
<span id=qr-filesize></span>
|
||||||
<span id=qr-extras-container>
|
<span id=qr-extras-container>
|
||||||
<a id=qr-filerm href=javascript:; title='Remove file'>×</a>
|
<a id=qr-filerm href=javascript:; title='Remove file'>×</a>
|
||||||
<a id=dump-button title='Dump list'>+</a>
|
<a id=dump-button title='Dump list'>+</a>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 335 B |
|
Before Width: | Height: | Size: 456 B After Width: | Height: | Size: 384 B |
|
Before Width: | Height: | Size: 323 B After Width: | Height: | Size: 309 B |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 376 B |
|
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 309 B |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 377 B |
|
Before Width: | Height: | Size: 232 B After Width: | Height: | Size: 189 B |
|
Before Width: | Height: | Size: 232 B After Width: | Height: | Size: 216 B |
|
Before Width: | Height: | Size: 232 B After Width: | Height: | Size: 216 B |
|
Before Width: | Height: | Size: 114 B After Width: | Height: | Size: 113 B |
|
Before Width: | Height: | Size: 172 B After Width: | Height: | Size: 133 B |
|
Before Width: | Height: | Size: 274 B After Width: | Height: | Size: 254 B |
|
Before Width: | Height: | Size: 172 B After Width: | Height: | Size: 133 B |
|
Before Width: | Height: | Size: 274 B After Width: | Height: | Size: 256 B |
|
Before Width: | Height: | Size: 172 B After Width: | Height: | Size: 133 B |
|
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 253 B |
|
Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 254 B |
|
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 300 B |
|
Before Width: | Height: | Size: 281 B After Width: | Height: | Size: 263 B |
|
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 311 B |
|
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 262 B |
|
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 311 B |
@ -44,10 +44,8 @@ $.formData = (form) ->
|
|||||||
return new FormData form
|
return new FormData form
|
||||||
fd = new FormData()
|
fd = new FormData()
|
||||||
for key, val of form when val
|
for key, val of form when val
|
||||||
# XXX GM bug
|
if typeof val is 'object' and 'newName' of val
|
||||||
# if val instanceof Blob
|
fd.append key, val, val.newName
|
||||||
if val.size and val.name
|
|
||||||
fd.append key, val, val.name
|
|
||||||
else
|
else
|
||||||
fd.append key, val
|
fd.append key, val
|
||||||
fd
|
fd
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
DataBoards = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads']
|
|
||||||
|
|
||||||
class DataBoard
|
class DataBoard
|
||||||
|
@keys = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads']
|
||||||
|
|
||||||
constructor: (@key, sync, dontClean) ->
|
constructor: (@key, sync, dontClean) ->
|
||||||
@data = Conf[key]
|
@data = Conf[key]
|
||||||
$.sync key, @onSync.bind @
|
$.sync key, @onSync
|
||||||
@clean() unless dontClean
|
@clean() unless dontClean
|
||||||
return unless sync
|
return unless sync
|
||||||
# Chrome also fires the onChanged callback on the current tab,
|
# Chrome also fires the onChanged callback on the current tab,
|
||||||
@ -88,6 +88,6 @@ class DataBoard
|
|||||||
@deleteIfEmpty {boardID}
|
@deleteIfEmpty {boardID}
|
||||||
@save()
|
@save()
|
||||||
|
|
||||||
onSync: (data) ->
|
onSync: (data) =>
|
||||||
@data = data or boards: {}
|
@data = data or boards: {}
|
||||||
@sync?()
|
@sync?()
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name <%= meta.name %>
|
// @name <%= meta.name %>
|
||||||
// @version <%= version %>
|
// @version <%= version %>
|
||||||
|
// @minGMVer 1.13
|
||||||
|
// @minFFVer 22
|
||||||
// @namespace <%= name %>
|
// @namespace <%= name %>
|
||||||
// @description <%= description %>
|
// @description <%= description %>
|
||||||
// @license MIT; <%= meta.repo %>blob/<%= meta.mainBranch %>/LICENSE
|
// @license MIT; <%= meta.repo %>blob/<%= meta.mainBranch %>/LICENSE
|
||||||
|
|||||||
@ -3,8 +3,7 @@ ImageExpand =
|
|||||||
return if g.VIEW is 'catalog' or !Conf['Image Expansion']
|
return if g.VIEW is 'catalog' or !Conf['Image Expansion']
|
||||||
|
|
||||||
@EAI = $.el 'a',
|
@EAI = $.el 'a',
|
||||||
className: 'expand-all-shortcut'
|
className: 'expand-all-shortcut icon-resize-full'
|
||||||
textContent: 'EAI'
|
|
||||||
title: 'Expand All Images'
|
title: 'Expand All Images'
|
||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
$.on @EAI, 'click', ImageExpand.cb.toggleAll
|
$.on @EAI, 'click', ImageExpand.cb.toggleAll
|
||||||
@ -33,11 +32,11 @@ ImageExpand =
|
|||||||
toggleAll: ->
|
toggleAll: ->
|
||||||
$.event 'CloseMenu'
|
$.event 'CloseMenu'
|
||||||
if ImageExpand.on = $.hasClass ImageExpand.EAI, 'expand-all-shortcut'
|
if ImageExpand.on = $.hasClass ImageExpand.EAI, 'expand-all-shortcut'
|
||||||
ImageExpand.EAI.className = 'contract-all-shortcut'
|
ImageExpand.EAI.className = 'contract-all-shortcut icon-resize-small'
|
||||||
ImageExpand.EAI.title = 'Contract All Images'
|
ImageExpand.EAI.title = 'Contract All Images'
|
||||||
func = ImageExpand.expand
|
func = ImageExpand.expand
|
||||||
else
|
else
|
||||||
ImageExpand.EAI.className = 'expand-all-shortcut'
|
ImageExpand.EAI.className = 'expand-all-shortcut icon-resize-full'
|
||||||
ImageExpand.EAI.title = 'Expand All Images'
|
ImageExpand.EAI.title = 'Expand All Images'
|
||||||
func = ImageExpand.contract
|
func = ImageExpand.contract
|
||||||
for ID, post of g.posts
|
for ID, post of g.posts
|
||||||
|
|||||||
@ -2,24 +2,23 @@ Linkify =
|
|||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW is 'catalog' or not Conf['Linkify']
|
return if g.VIEW is 'catalog' or not Conf['Linkify']
|
||||||
|
|
||||||
@regString =
|
@regString = ///(
|
||||||
///(
|
# http, magnet, ftp, etc
|
||||||
# http, magnet, ftp, etc
|
(https?|mailto|git|magnet|ftp|irc):(
|
||||||
(https?|mailto|git|magnet|ftp|irc):(
|
[a-z\d%/]
|
||||||
[a-z\d%/]
|
)
|
||||||
)
|
|
|
||||||
|
|
# This should account for virtually all links posted without http:
|
||||||
# This should account for virtually all links posted without http:
|
[-a-z\d]+[.](
|
||||||
[-a-z\d]+[.](
|
aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2}
|
||||||
aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2}
|
)(/|(?!.))
|
||||||
)(/|(?!.))
|
|
|
||||||
|
|
# IPv4 Addresses
|
||||||
# IPv4 Addresses
|
[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}
|
||||||
[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}
|
|
|
||||||
|
|
# E-mails
|
||||||
# E-mails
|
[-\w\d.@]+@[a-z\d.-]+\.[a-z\d]
|
||||||
[-\w\d.@]+@[a-z\d.-]+\.[a-z\d]
|
)///i
|
||||||
)///i
|
|
||||||
|
|
||||||
if Conf['Comment Expansion']
|
if Conf['Comment Expansion']
|
||||||
ExpandComment.callbacks.push @node
|
ExpandComment.callbacks.push @node
|
||||||
|
|||||||
@ -32,13 +32,9 @@ Fourchan =
|
|||||||
cb: @math
|
cb: @math
|
||||||
code: ->
|
code: ->
|
||||||
return if @isClone
|
return if @isClone
|
||||||
for pre in $$ '.prettyprint', @nodes.comment
|
for pre in $$ '.prettyprint:not(.prettyprinted)', @nodes.comment
|
||||||
# Don't pretty print twice:
|
$.event 'prettyprint', pre, window
|
||||||
# Might need a better way to detect if a .prettyprint
|
$.addClass pre, 'prettyprinted'
|
||||||
# is already pretty-printed. We can't just look for spans
|
|
||||||
# since 4chan inserts its quotes and whatnot inside.
|
|
||||||
unless $ '.pln', pre
|
|
||||||
$.event 'prettyprint', pre, window
|
|
||||||
return
|
return
|
||||||
math: ->
|
math: ->
|
||||||
return if @isClone or !$ '.math', @nodes.comment
|
return if @isClone or !$ '.math', @nodes.comment
|
||||||
|
|||||||
@ -45,4 +45,6 @@ Favicon =
|
|||||||
Favicon.unread = Favicon.unreadNSFW
|
Favicon.unread = Favicon.unreadNSFW
|
||||||
Favicon.unreadY = Favicon.unreadNSFWY
|
Favicon.unreadY = Favicon.unreadNSFWY
|
||||||
|
|
||||||
|
empty: 'data:image/gif;base64,<%= grunt.file.read("src/General/img/favicons/empty.gif", {encoding: "base64"}) %>'
|
||||||
dead: 'data:image/gif;base64,<%= grunt.file.read("src/General/img/favicons/dead.gif", {encoding: "base64"}) %>'
|
dead: 'data:image/gif;base64,<%= grunt.file.read("src/General/img/favicons/dead.gif", {encoding: "base64"}) %>'
|
||||||
|
logo: 'data:image/png;base64,<%= grunt.file.read("src/General/img/icon128.png", {encoding: "base64"}) %>'
|
||||||
|
|||||||
@ -219,14 +219,14 @@ ThreadUpdater =
|
|||||||
'The thread is not a sticky anymore.'
|
'The thread is not a sticky anymore.'
|
||||||
else
|
else
|
||||||
'The thread is not closed anymore.'
|
'The thread is not closed anymore.'
|
||||||
new Notification 'info', message, 30
|
new Notice 'info', message, 30
|
||||||
$.rm $ ".#{titleLC}Icon", ThreadUpdater.thread.OP.nodes.info
|
$.rm $ ".#{titleLC}Icon", ThreadUpdater.thread.OP.nodes.info
|
||||||
return
|
return
|
||||||
message = if title is 'Sticky'
|
message = if title is 'Sticky'
|
||||||
'The thread is now a sticky.'
|
'The thread is now a sticky.'
|
||||||
else
|
else
|
||||||
'The thread is now closed.'
|
'The thread is now closed.'
|
||||||
new Notification 'info', message, 30
|
new Notice 'info', message, 30
|
||||||
icon = $.el 'img',
|
icon = $.el 'img',
|
||||||
src: "//static.4chan.org/image/#{titleLC}.gif"
|
src: "//static.4chan.org/image/#{titleLC}.gif"
|
||||||
alt: title
|
alt: title
|
||||||
|
|||||||
@ -90,10 +90,25 @@ Unread =
|
|||||||
|
|
||||||
addPostQuotingYou: (post) ->
|
addPostQuotingYou: (post) ->
|
||||||
return unless QR.db
|
return unless QR.db
|
||||||
for quotelink in post.nodes.quotelinks
|
for quotelink in post.nodes.quotelinks when QR.db.get Get.postDataFromLink quotelink
|
||||||
if QR.db.get Get.postDataFromLink quotelink
|
Unread.postsQuotingYou.push post
|
||||||
Unread.postsQuotingYou.push post
|
Unread.openNotification post
|
||||||
return
|
return
|
||||||
|
openNotification: (post) ->
|
||||||
|
return unless d.hidden
|
||||||
|
name = if Conf['Anonymize']
|
||||||
|
'Anonymous'
|
||||||
|
else
|
||||||
|
$('.nameBlock', post.nodes.info).textContent.trim()
|
||||||
|
notif = new Notification "#{name} replied to you.",
|
||||||
|
body: post.info.comment
|
||||||
|
icon: Favicon.logo
|
||||||
|
notif.onclick = ->
|
||||||
|
Header.scrollToPost post.nodes.root
|
||||||
|
window.focus()
|
||||||
|
setTimeout ->
|
||||||
|
notif.close()
|
||||||
|
, 5 * $.SECOND
|
||||||
|
|
||||||
onUpdate: (e) ->
|
onUpdate: (e) ->
|
||||||
if e.detail[404]
|
if e.detail[404]
|
||||||
|
|||||||
@ -45,6 +45,7 @@ QR =
|
|||||||
innerHTML: "<a href=javascript:; class='qr-link'>#{if g.VIEW is 'thread' then 'Reply to Thread' else 'Start a Thread'}</a>"
|
innerHTML: "<a href=javascript:; class='qr-link'>#{if g.VIEW is 'thread' then 'Reply to Thread' else 'Start a Thread'}</a>"
|
||||||
className: "qr-link-container"
|
className: "qr-link-container"
|
||||||
$.on link.firstChild, 'click', ->
|
$.on link.firstChild, 'click', ->
|
||||||
|
|
||||||
$.event 'CloseMenu'
|
$.event 'CloseMenu'
|
||||||
QR.open()
|
QR.open()
|
||||||
QR.nodes.com.focus()
|
QR.nodes.com.focus()
|
||||||
@ -109,7 +110,13 @@ QR =
|
|||||||
focusin: ->
|
focusin: ->
|
||||||
$.addClass QR.nodes.el, 'has-focus'
|
$.addClass QR.nodes.el, 'has-focus'
|
||||||
focusout: ->
|
focusout: ->
|
||||||
|
<% if (type === 'crx') { %>
|
||||||
$.rmClass QR.nodes.el, 'has-focus'
|
$.rmClass QR.nodes.el, 'has-focus'
|
||||||
|
<% } else { %>
|
||||||
|
$.queueTask ->
|
||||||
|
return if $.x 'ancestor::div[@id="qr"]', d.activeElement
|
||||||
|
$.rmClass QR.nodes.el, 'has-focus'
|
||||||
|
<% } %>
|
||||||
hide: ->
|
hide: ->
|
||||||
d.activeElement.blur()
|
d.activeElement.blur()
|
||||||
$.addClass QR.nodes.el, 'autohide'
|
$.addClass QR.nodes.el, 'autohide'
|
||||||
@ -134,15 +141,29 @@ QR =
|
|||||||
# Focus the captcha input on captcha error.
|
# Focus the captcha input on captcha error.
|
||||||
QR.captcha.nodes.input.focus()
|
QR.captcha.nodes.input.focus()
|
||||||
if Conf['Captcha Warning Notifications']
|
if Conf['Captcha Warning Notifications']
|
||||||
QR.notifications.push new Notification 'warning', el
|
QR.notify el
|
||||||
else
|
else
|
||||||
$.addClass QR.captcha.nodes.input, 'error'
|
$.addClass QR.captcha.nodes.input, 'error'
|
||||||
$.on QR.captcha.nodes.input, 'keydown', ->
|
$.on QR.captcha.nodes.input, 'keydown', ->
|
||||||
$.rmClass QR.captcha.nodes.input, 'error'
|
$.rmClass QR.captcha.nodes.input, 'error'
|
||||||
else
|
else
|
||||||
QR.notifications.push new Notification 'warning', el
|
QR.notify el
|
||||||
alert el.textContent if d.hidden
|
alert el.textContent if d.hidden
|
||||||
|
|
||||||
|
notify: (el) ->
|
||||||
|
notice = new Notice 'warning', el
|
||||||
|
QR.notifications.push notice
|
||||||
|
return unless d.hidden
|
||||||
|
notif = new Notification 'Quick reply warning',
|
||||||
|
body: el.textContent
|
||||||
|
icon: Favicon.logo
|
||||||
|
notif.onclick = -> window.focus()
|
||||||
|
notif.onclose = -> notice.close()
|
||||||
|
setTimeout ->
|
||||||
|
notif.onclose = null
|
||||||
|
notif.close()
|
||||||
|
, 5 * $.SECOND
|
||||||
|
|
||||||
notifications: []
|
notifications: []
|
||||||
cleanNotifications: ->
|
cleanNotifications: ->
|
||||||
for notification in QR.notifications
|
for notification in QR.notifications
|
||||||
@ -439,7 +460,11 @@ QR =
|
|||||||
QR.fileInput files
|
QR.fileInput files
|
||||||
|
|
||||||
openFileInput: (e) ->
|
openFileInput: (e) ->
|
||||||
return if e.keyCode and e.keyCode isnt 32
|
e.stopPropagation()
|
||||||
|
if e.shiftKey and e.type is 'click'
|
||||||
|
return QR.selected.rmFile()
|
||||||
|
return if e.target.nodeName is 'INPUT' or (e.keyCode and not [32, 13].contains e.keyCode) or e.ctrlKey
|
||||||
|
e.preventDefault()
|
||||||
QR.nodes.fileInput.click()
|
QR.nodes.fileInput.click()
|
||||||
|
|
||||||
fileInput: (files) ->
|
fileInput: (files) ->
|
||||||
@ -500,8 +525,8 @@ QR =
|
|||||||
for elm in $$ '*', el
|
for elm in $$ '*', el
|
||||||
$.on elm, 'blur', QR.focusout
|
$.on elm, 'blur', QR.focusout
|
||||||
$.on elm, 'focus', QR.focusin
|
$.on elm, 'focus', QR.focusin
|
||||||
<% } %>
|
<% } %>
|
||||||
$.on el, 'click', @select.bind @
|
$.on el, 'click', @select
|
||||||
$.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()
|
||||||
$.on @nodes.spoiler, 'change', (e) =>
|
$.on @nodes.spoiler, 'change', (e) =>
|
||||||
@ -565,18 +590,17 @@ QR =
|
|||||||
lock: (lock=true) ->
|
lock: (lock=true) ->
|
||||||
@isLocked = lock
|
@isLocked = lock
|
||||||
return unless @ is QR.selected
|
return unless @ is QR.selected
|
||||||
for name in ['thread', 'name', 'email', 'sub', 'com', 'spoiler']
|
for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler']
|
||||||
QR.nodes[name].disabled = lock
|
QR.nodes[name].disabled = lock
|
||||||
@nodes.rm.style.visibility =
|
@nodes.rm.style.visibility = if lock then 'hidden' else ''
|
||||||
QR.nodes.fileRM.style.visibility = if lock then 'hidden' else ''
|
(if lock then $.off else $.on) QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput
|
||||||
(if lock then $.off else $.on) QR.nodes.filename.parentNode, 'click', QR.openFileInput
|
|
||||||
@nodes.spoiler.disabled = lock
|
@nodes.spoiler.disabled = lock
|
||||||
@nodes.el.draggable = !lock
|
@nodes.el.draggable = !lock
|
||||||
|
|
||||||
unlock: ->
|
unlock: ->
|
||||||
@lock false
|
@lock false
|
||||||
|
|
||||||
select: ->
|
select: =>
|
||||||
if QR.selected
|
if QR.selected
|
||||||
QR.selected.nodes.el.id = null
|
QR.selected.nodes.el.id = null
|
||||||
QR.selected.forceSave()
|
QR.selected.forceSave()
|
||||||
@ -592,7 +616,7 @@ QR =
|
|||||||
|
|
||||||
load: ->
|
load: ->
|
||||||
# Load this post's values.
|
# Load this post's values.
|
||||||
for name in ['thread', 'name', 'email', 'sub', 'com']
|
for name in ['thread', 'name', 'email', 'sub', 'com', 'filename']
|
||||||
QR.nodes[name].value = @[name] or null
|
QR.nodes[name].value = @[name] or null
|
||||||
@showFileData()
|
@showFileData()
|
||||||
QR.characterCount()
|
QR.characterCount()
|
||||||
@ -613,18 +637,27 @@ QR =
|
|||||||
# during the last 5 seconds of the cooldown.
|
# during the last 5 seconds of the cooldown.
|
||||||
if QR.cooldown.auto and @ is QR.posts[0] and 0 < QR.cooldown.seconds <= 5
|
if QR.cooldown.auto and @ is QR.posts[0] and 0 < QR.cooldown.seconds <= 5
|
||||||
QR.cooldown.auto = false
|
QR.cooldown.auto = false
|
||||||
|
when 'filename'
|
||||||
|
return unless @file
|
||||||
|
@file.newName = @filename.replace /[/\\]/g, '-'
|
||||||
|
unless /\.(jpe?g|png|gif|pdf|sfw)$/i.test @filename
|
||||||
|
# 4chan will truncate the filename if it has no extension,
|
||||||
|
# but it will always replace the extension by the correct one,
|
||||||
|
# so we suffix it with '.jpg' when needed.
|
||||||
|
@file.newName += '.jpg'
|
||||||
|
@updateFilename()
|
||||||
|
|
||||||
forceSave: ->
|
forceSave: ->
|
||||||
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 ['thread', 'name', 'email', 'sub', 'com', 'spoiler']
|
for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler']
|
||||||
@save QR.nodes[name]
|
@save QR.nodes[name]
|
||||||
return
|
return
|
||||||
|
|
||||||
setFile: (@file) ->
|
setFile: (@file) ->
|
||||||
@filename = "#{file.name} (#{$.bytesToString file.size})"
|
@filename = file.name
|
||||||
@nodes.el.title = @filename
|
@filesize = $.bytesToString file.size
|
||||||
@nodes.label.hidden = false if QR.spoiler
|
@nodes.label.hidden = false if QR.spoiler
|
||||||
URL.revokeObjectURL @URL
|
URL.revokeObjectURL @URL
|
||||||
@showFileData()
|
@showFileData()
|
||||||
@ -668,18 +701,27 @@ QR =
|
|||||||
img.src = fileURL
|
img.src = fileURL
|
||||||
|
|
||||||
rmFile: ->
|
rmFile: ->
|
||||||
|
return if @isLocked
|
||||||
delete @file
|
delete @file
|
||||||
delete @filename
|
delete @filename
|
||||||
|
delete @filesize
|
||||||
@nodes.el.title = null
|
@nodes.el.title = null
|
||||||
@nodes.el.style.backgroundImage = null
|
@nodes.el.style.backgroundImage = null
|
||||||
@nodes.label.hidden = true if QR.spoiler
|
@nodes.label.hidden = true if QR.spoiler
|
||||||
@showFileData()
|
@showFileData()
|
||||||
URL.revokeObjectURL @URL
|
URL.revokeObjectURL @URL
|
||||||
|
|
||||||
|
updateFilename: ->
|
||||||
|
long = "#{@filename} (#{@filesize})"
|
||||||
|
@nodes.el.title = long
|
||||||
|
return unless @ is QR.selected
|
||||||
|
QR.nodes.filename.title = long
|
||||||
|
|
||||||
showFileData: ->
|
showFileData: ->
|
||||||
if @file
|
if @file
|
||||||
QR.nodes.filename.textContent = @filename
|
@updateFilename()
|
||||||
QR.nodes.filename.title = @filename
|
QR.nodes.filename.value = @filename
|
||||||
|
QR.nodes.filesize.textContent = @filesize
|
||||||
QR.nodes.spoiler.checked = @spoiler
|
QR.nodes.spoiler.checked = @spoiler
|
||||||
$.addClass QR.nodes.fileSubmit, 'has-file'
|
$.addClass QR.nodes.fileSubmit, 'has-file'
|
||||||
else
|
else
|
||||||
@ -845,44 +887,46 @@ QR =
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
dialog: ->
|
dialog: ->
|
||||||
dialog = UI.dialog 'qr', 'top:0;right:0;', """
|
|
||||||
<%= grunt.file.read('src/General/html/Features/QuickReply.html').replace(/>\s+</g, '><').trim() %>
|
|
||||||
"""
|
|
||||||
|
|
||||||
QR.nodes = nodes =
|
QR.nodes = nodes =
|
||||||
el: dialog
|
el: dialog = UI.dialog 'qr', 'top:0;right:0;', """
|
||||||
move: $ '.move', dialog
|
<%= grunt.file.read('src/General/html/Features/QuickReply.html').replace(/>\s+</g, '><').trim() %>
|
||||||
autohide: $ '#autohide', dialog
|
"""
|
||||||
thread: $ 'select', dialog
|
|
||||||
close: $ '.close', dialog
|
nodes[key] = $ value, dialog for key, value of {
|
||||||
form: $ 'form', dialog
|
move: '.move'
|
||||||
dumpButton: $ '#dump-button', dialog
|
autohide: '#autohide'
|
||||||
name: $ '[data-name=name]', dialog
|
thread: 'select'
|
||||||
email: $ '[data-name=email]', dialog
|
threadPar: '#qr-thread-select'
|
||||||
sub: $ '[data-name=sub]', dialog
|
close: '.close'
|
||||||
com: $ '[data-name=com]', dialog
|
form: 'form'
|
||||||
dumpList: $ '#dump-list', dialog
|
dumpButton: '#dump-button'
|
||||||
addPost: $ '#add-post', dialog
|
name: '[data-name=name]'
|
||||||
charCount: $ '#char-count', dialog
|
email: '[data-name=email]'
|
||||||
fileSubmit: $ '#file-n-submit', dialog
|
sub: '[data-name=sub]'
|
||||||
filename: $ '#qr-filename', dialog
|
com: '[data-name=com]'
|
||||||
fileRM: $ '#qr-filerm', dialog
|
dumpList: '#dump-list'
|
||||||
fileExtras: $ '#qr-extras-container', dialog
|
addPost: '#add-post'
|
||||||
spoiler: $ '#qr-file-spoiler', dialog
|
charCount: '#char-count'
|
||||||
status: $ '[type=submit]', dialog
|
fileSubmit: '#file-n-submit'
|
||||||
fileInput: $ '[type=file]', dialog
|
filename: '#qr-filename'
|
||||||
|
filesize: '#qr-filesize'
|
||||||
|
fileRM: '#qr-filerm'
|
||||||
|
fileExtras: '#qr-extras-container'
|
||||||
|
spoiler: '#qr-file-spoiler'
|
||||||
|
spoilerPar: '#qr-spoiler-label'
|
||||||
|
status: '[type=submit]'
|
||||||
|
fileInput: '[type=file]'
|
||||||
|
}
|
||||||
|
|
||||||
|
check =
|
||||||
|
jpg: 'image/jpeg'
|
||||||
|
pdf: 'application/pdf'
|
||||||
|
swf: 'application/x-shockwave-flash'
|
||||||
|
|
||||||
# Allow only this board's supported files.
|
# Allow only this board's supported files.
|
||||||
mimeTypes = $('ul.rules > li').textContent.trim().match(/: (.+)/)[1].toLowerCase().replace /\w+/g, (type) ->
|
mimeTypes = $('ul.rules > li').textContent.trim().match(/: (.+)/)[1].toLowerCase().replace /\w+/g, (type) ->
|
||||||
switch type
|
check[type] or "image/#{type}"
|
||||||
when 'jpg'
|
|
||||||
'image/jpeg'
|
|
||||||
when 'pdf'
|
|
||||||
'application/pdf'
|
|
||||||
when 'swf'
|
|
||||||
'application/x-shockwave-flash'
|
|
||||||
else
|
|
||||||
"image/#{type}"
|
|
||||||
QR.mimeTypes = mimeTypes.split ', '
|
QR.mimeTypes = mimeTypes.split ', '
|
||||||
# Add empty mimeType to avoid errors with URLs selected in Window's file dialog.
|
# Add empty mimeType to avoid errors with URLs selected in Window's file dialog.
|
||||||
QR.mimeTypes.push ''
|
QR.mimeTypes.push ''
|
||||||
@ -924,8 +968,9 @@ QR =
|
|||||||
$.on elm, 'blur', QR.focusout
|
$.on elm, 'blur', QR.focusout
|
||||||
$.on elm, 'focus', QR.focusin
|
$.on elm, 'focus', QR.focusin
|
||||||
<% } %>
|
<% } %>
|
||||||
$.on dialog, 'focusin', QR.focusin
|
$.on dialog, 'focusin', QR.focusin
|
||||||
$.on dialog, 'focusout', QR.focusout
|
$.on dialog, 'focusout', QR.focusout
|
||||||
|
|
||||||
$.on nodes.autohide, 'change', QR.toggleHide
|
$.on nodes.autohide, 'change', QR.toggleHide
|
||||||
$.on nodes.close, 'click', QR.close
|
$.on nodes.close, 'click', QR.close
|
||||||
$.on nodes.dumpButton, 'click', -> nodes.el.classList.toggle 'dump'
|
$.on nodes.dumpButton, 'click', -> nodes.el.classList.toggle 'dump'
|
||||||
@ -936,7 +981,7 @@ QR =
|
|||||||
$.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
|
||||||
# save selected post's data
|
# save selected post's data
|
||||||
items = ['name', 'email', 'sub', 'com']
|
items = ['name', 'email', 'sub', 'com', 'filename']
|
||||||
i = 0
|
i = 0
|
||||||
while name = items[i++]
|
while name = items[i++]
|
||||||
$.on nodes[name], 'input', -> QR.selected.save @
|
$.on nodes[name], 'input', -> QR.selected.save @
|
||||||
@ -1186,5 +1231,6 @@ QR =
|
|||||||
QR.req.abort()
|
QR.req.abort()
|
||||||
delete QR.req
|
delete QR.req
|
||||||
QR.posts[0].unlock()
|
QR.posts[0].unlock()
|
||||||
QR.notifications.push new Notification 'info', 'QR upload aborted.', 5
|
QR.cooldown.auto = false
|
||||||
|
QR.notifications.push new Notice 'info', 'QR upload aborted.', 5
|
||||||
QR.status()
|
QR.status()
|
||||||
|
|||||||
@ -26,6 +26,7 @@ QuoteOP =
|
|||||||
|
|
||||||
{fullID} = (if @isClone then @context else @).thread
|
{fullID} = (if @isClone then @context else @).thread
|
||||||
# add (OP) to quotes quoting this context's OP.
|
# add (OP) to quotes quoting this context's OP.
|
||||||
|
|
||||||
return unless quotes.contains fullID
|
return unless quotes.contains fullID
|
||||||
i = 0
|
i = 0
|
||||||
while quotelink = quotelinks[i++]
|
while quotelink = quotelinks[i++]
|
||||||
|
|||||||
@ -18,7 +18,7 @@ Quotify =
|
|||||||
return
|
return
|
||||||
|
|
||||||
parseDeadlink: (deadlink) ->
|
parseDeadlink: (deadlink) ->
|
||||||
if deadlink.parentNode.className is 'prettyprint'
|
if $.hasClass deadlink.parentNode, 'prettyprint'
|
||||||
# Don't quotify deadlinks inside code tags,
|
# Don't quotify deadlinks inside code tags,
|
||||||
# un-`span` them.
|
# un-`span` them.
|
||||||
# This won't be necessary once 4chan
|
# This won't be necessary once 4chan
|
||||||
|
|||||||