Merge branch 'v3' into av2

Conflicts:
	css/style.css
	package.json
This commit is contained in:
Zixaphir 2013-04-09 08:33:09 -07:00
commit 8f53b3275c
6 changed files with 130 additions and 41 deletions

View File

@ -1,3 +1,8 @@
### 3.0.2 - *2013-04-09*
- Added a setting in the Header's menu to move it at the bottom of the screen.
- Added Cooldown setting back in.
- Fixed the Header going above posts when following quotelinks for example.
- Fixed a bug where dead quotelinks would disappear.
### 3.0.1 - *2013-04-08*

View File

@ -116,7 +116,8 @@
'Open Post in New Tab': [true, 'Open new threads or replies to a thread from the index in a new tab.'],
'Remember Subject': [false, 'Remember the subject field, instead of resetting after posting.'],
'Remember Spoiler': [false, 'Remember the spoiler state, instead of resetting after posting.'],
'Hide Original Post Form': [true, 'Hide the normal post form.']
'Hide Original Post Form': [true, 'Hide the normal post form.'],
'Cooldown': [true, 'Prevent "flood detected" errors.']
},
'Quote Links': {
'Quote Backlinks': [true, 'Add quote backlinks.'],
@ -3546,7 +3547,7 @@
return position;
};
if (_conf["Icon Orientation"] === "horizontal") {
position = aligner(2, [notCatalog, _conf['Slideout Navigation'] !== 'hide', _conf['Announcements'] === 'slideout' && $('#globalMessage', d.body), notCatalog && _conf['Slideout Watcher'] && _conf['Thread Watcher'], $('#navtopright .exlinksOptionsLink', d.body), notCatalog && $('body > a[style="cursor: pointer; float: right;"]', d.body), notEither && _conf['Image Expansion'], notEither && _conf['Image Expansion'], notEither, g.REPLY, notEither && _conf['Fappe Tyme'], navlinks = ((!g.REPLY && _conf['Index Navigation']) || (g.REPLY && _conf['Reply Navigation'])) && notCatalog, navlinks]);
position = aligner(2, [notCatalog, _conf['Slideout Navigation'] !== 'hide', _conf['Announcements'] === 'slideout' && $('#globalMessage', d.body), notCatalog && _conf['Slideout Watcher'] && _conf['Thread Watcher'], $('#navtopright .exlinksOptionsLink', d.body), notCatalog && $('body > a[style="cursor: pointer; float: right;"]', d.body), notEither && _conf['Image Expansion'], notEither && _conf['Image Expansion'], notEither, g.VIEW === 'thread', notEither && _conf['Fappe Tyme'], navlinks = ((!g.VIEW === 'thread' && _conf['Index Navigation']) || (g.VIEW === 'thread' && _conf['Reply Navigation'])) && notCatalog, navlinks]);
iconOffset = position[position.length - 1] - (_conf['4chan SS Navigation'] ? 0 : Style.sidebar + parseInt(_conf["Right Thread Padding"], 10));
if (iconOffset < 0) {
iconOffset = 0;
@ -3556,8 +3557,8 @@
css += "/* Updater + Stats */\n#updater,\n#thread-stats {\n " + align + ": " + (_conf["Updater Position"] === "bottom" && !_conf["Hide Delete UI"] ? 23 : 2) + "px !important;\n " + Style.sidebarLocation[1] + ": auto !important;\n top: auto !important;\n bottom: auto !important;\n " + (_conf["Updater Position"] === 'top' ? "top: 18px !important" : "bottom: 0 !important") + ";\n}";
}
} else {
position = aligner(2 + (_conf["4chan Banner"] === "at sidebar top" ? Style.logoOffset + 19 : 0), [notEither && _conf['Image Expansion'], notEither && _conf['Image Expansion'], notCatalog, _conf['Slideout Navigation'] !== 'hide', _conf['Announcements'] === 'slideout' && $('#globalMessage', d.body), notCatalog && _conf['Slideout Watcher'] && _conf['Thread Watcher'], notCatalog && $('body > a[style="cursor: pointer; float: right;"]', d.body), $('#navtopright .exlinksOptionsLink', d.body), notEither, g.REPLY, notEither && _conf['Fappe Tyme'], navlinks = ((!g.REPLY && _conf['Index Navigation']) || (g.REPLY && _conf['Reply Navigation'])) && notCatalog, navlinks]);
iconOffset = (g.REPLY && _conf['Prefetch'] ? 250 + Style.sidebarOffset.W : 20 + (g.REPLY && _conf['Updater Position'] === 'top' ? 100 : 0)) - (_conf['4chan SS Navigation'] ? 0 : Style.sidebar + parseInt(_conf[align.capitalize() + " Thread Padding"], 10));
position = aligner(2 + (_conf["4chan Banner"] === "at sidebar top" ? Style.logoOffset + 19 : 0), [notEither && _conf['Image Expansion'], notEither && _conf['Image Expansion'], notCatalog, _conf['Slideout Navigation'] !== 'hide', _conf['Announcements'] === 'slideout' && $('#globalMessage', d.body), notCatalog && _conf['Slideout Watcher'] && _conf['Thread Watcher'], notCatalog && $('body > a[style="cursor: pointer; float: right;"]', d.body), $('#navtopright .exlinksOptionsLink', d.body), notEither, g.VIEW === 'thread', notEither && _conf['Fappe Tyme'], navlinks = ((!g.VIEW === 'thread' && _conf['Index Navigation']) || (g.VIEW === 'thread' && _conf['Reply Navigation'])) && notCatalog, navlinks]);
iconOffset = (g.VIEW === 'thread' && _conf['Prefetch'] ? 250 + Style.sidebarOffset.W : 20 + (g.VIEW === 'thread' && _conf['Updater Position'] === 'top' ? 100 : 0)) - (_conf['4chan SS Navigation'] ? 0 : Style.sidebar + parseInt(_conf[align.capitalize() + " Thread Padding"], 10));
css += "/* Expand Images */\n#imgControls .expand-all-shortcut,\n#imgControls .contract-all-shortcut {\n top: " + position[i++] + "px;\n}\n/* Expand Images Menu */\n#imgControls .menu-button {\n top: " + position[i++] + "px;\n}\n/* 4chan X Options */\n#appchanOptions {\n top: " + position[i++] + "px;\n}\n/* Slideout Navigation */\n#boardNavDesktopFoot,\n#boardNavDesktopFoot::after {\n top: " + position[i++] + "px;\n}\n/* Global Message */\n#globalMessage,\n#globalMessage::after {\n top: " + position[i++] + "px;\n}\n/* Watcher */\n" + (_conf["Slideout Watcher"] ? "#watcher, #watcher::after" : "") + " {\n top: " + position[i++] + "px !important;\n}\n/* 4sight */\nbody > a[style=\"cursor: pointer; float: right;\"]::after {\n top: " + position[i++] + "px;\n}\n/* ExLinks */\n#navtopright .exlinksOptionsLink::after {\n top: " + position[i++] + "px;\n}\n/* 4chan Catalog */\n#catalog::after {\n top: " + position[i++] + "px;\n}\n/* Back */\ndiv.navLinks > a:first-of-type::after {\n top: " + position[i++] + "px;\n}\n/* Fappe Tyme */\n#fappeTyme {\n top: " + position[i++] + "px;\n}\n/* Thread Navigation Links */\n#navlinks a:first-of-type {\n top: " + position[i++] + "px !important;\n}\n#navlinks a:last-of-type {\n top: " + position[i++] + "px !important;\n}\n#prefetch {\n width: " + (248 + Style.sidebarOffset.W) + "px;\n " + align + ": 2px;\n top: 0;\n text-align: " + Style.sidebarLocation[1] + ";\n}\n#navlinks a,\n#navtopright .exlinksOptionsLink::after,\n#appchanOptions,\n#boardNavDesktopFoot::after,\n#globalMessage::after,\n#imgControls .expand-all-shortcut,\n#imgControls .contract-all-shortcut,\n#imgControls .menu-button,\n#fappeTyme,\n" + (_conf["Slideout Watcher"] ? "#watcher::after," : "") + "\nbody > a[style=\"cursor: pointer; float: right;\"]::after,\n#catalog::after,\ndiv.navLinks > a:first-of-type::after {\n " + align + ": 3px !important;\n}\n#boardNavDesktopFoot,\n#globalMessage,\n#watcher {\n width: " + (233 + Style.sidebarOffset.W) + "px !important;\n " + align + ": 18px !important;\n}\n" + (_conf['Boards Navigation'] === 'top' || _conf['Boards Navigation'] === 'sticky top' ? '#boardNavDesktop' : _conf['Pagination'] === 'top' || _conf['Pagination'] === 'sticky top' ? '.pagelist' : void 0) + " {\n " + (_conf['4chan SS Navigation'] ? "padding-" + align + ": " + iconOffset + "px;" : "margin-" + align + ": " + iconOffset + "px;") + "\n}";
if (_conf["Updater Position"] !== 'moveable') {
css += "/* Updater + Stats */\n#updater,\n#thread-stats {\n " + align + ": " + (_conf["Updater Position"] === "top" || !_conf["Hide Delete UI"] ? 23 : 2) + "px !important; \n " + Style.sidebarLocation[1] + ": auto !important;\n top: " + (_conf["Updater Position"] === "top" ? "-1px" : "auto") + " !important;\n bottom: " + (_conf["Updater Position"] === "bottom" ? "-2px" : "auto") + " !important;\n}";
@ -4220,7 +4221,7 @@
return null;
}
}
position = "" + (Conf['Mascot Position'] === 'bottom' || !(Conf['Mascot Position'] === "default" && Conf['Post Form Style'] === "fixed") ? 0 + ((!g.REPLY || Conf['Boards Navigation'] === 'sticky bottom') && Conf['4chan SS Navigation'] ? 1.6 : 0) : 20.3 + (!g.REPLY || !!$('#postForm input[name=spoiler]') ? 1.4 : 0) + (Conf['Show Post Form Header'] ? 1.5 : 0) + (Conf['Post Form Decorations'] ? 0.2 : 0)) + "em";
position = "" + (Conf['Mascot Position'] === 'bottom' || !(Conf['Mascot Position'] === "default" && Conf['Post Form Style'] === "fixed") ? 0 + ((!g.VIEW === 'thread' || Conf['Boards Navigation'] === 'sticky bottom') && Conf['4chan SS Navigation'] ? 1.6 : 0) : 20.3 + (!g.VIEW === 'thread' || !!$('#postForm input[name=spoiler]') ? 1.4 : 0) + (Conf['Show Post Form Header'] ? 1.5 : 0) + (Conf['Post Form Decorations'] ? 0.2 : 0)) + "em";
if (Conf['editMode']) {
if (!(mascot = editMascot || (mascot = Mascots[Conf["mascot"]]))) {
return;
@ -5429,6 +5430,7 @@
this.hover = $.el('div', {
id: 'hoverUI'
});
$.on(window, 'load hashchange', Header.hashScroll);
$.asap((function() {
return d.body;
}), function() {
@ -5538,6 +5540,24 @@
custom.hidden = !showBoardList;
return full.hidden = showBoardList;
},
hashScroll: function() {
var post;
if (!(post = $.id(this.location.hash.slice(1)))) {
return;
}
return Header.scrollToPost(post);
},
scrollToPost: function(post) {
var headRect, top;
top = post.getBoundingClientRect().top;
if (!Conf['Bottom header']) {
headRect = Header.bar.getBoundingClientRect();
top += -headRect.top - headRect.height;
}
return ($.engine === 'webkit' ? d.body : doc).scrollTop += top;
},
addShortcut: function(el) {
var shortcut;
@ -7205,8 +7225,12 @@
hl: function(delta, thread) {
var headRect, next, postEl, rect, replies, reply, root, topMargin, _i, _len;
headRect = Header.bar.getBoundingClientRect();
topMargin = headRect.top + headRect.height;
if (Conf['Bottom header']) {
topMargin = 0;
} else {
headRect = Header.toggle.getBoundingClientRect();
topMargin = headRect.top + headRect.height;
}
if (postEl = $('.reply.highlight', thread)) {
$.rmClass(postEl, 'highlight');
rect = postEl.getBoundingClientRect();
@ -7290,8 +7314,12 @@
getThread: function(full) {
var headRect, i, rect, thread, threads, topMargin, _i, _len;
headRect = Header.bar.getBoundingClientRect();
topMargin = headRect.top + headRect.height;
if (Conf['Bottom header']) {
topMargin = 0;
} else {
headRect = Header.toggle.getBoundingClientRect();
topMargin = headRect.top + headRect.height;
}
threads = $$('.thread:not([hidden])');
for (i = _i = 0, _len = threads.length; _i < _len; i = ++_i) {
thread = threads[i];
@ -8102,7 +8130,7 @@
posts.pop();
for (_i = 0, _len = posts.length; _i < _len; _i++) {
post = posts[_i];
$.addClass(post.nodes.root, 'qphl');
$.addClass(post.nodes.post, 'qphl');
}
}
quoterID = $.x('ancestor::*[@id][1]', this).id.match(/\d+$/)[0];
@ -8130,7 +8158,7 @@
_ref = [post].concat(post.clones);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
post = _ref[_i];
$.rmClass(post.nodes.root, 'qphl');
$.rmClass(post.nodes.post, 'qphl');
}
}
};
@ -8754,10 +8782,18 @@
});
},
node: function() {
if (!(this.file && this.file.isImage)) {
var thumb, _ref;
if (!((_ref = this.file) != null ? _ref.isImage : void 0)) {
return;
}
thumb = this.file.thumb;
$.on(thumb.parentNode, 'click', ImageExpand.cb.toggle);
if (this.isClone && $.hasClass(thumb, 'expanding')) {
ImageExpand.contract(this);
ImageExpand.expand(this);
return;
}
$.on(this.file.thumb.parentNode, 'click', ImageExpand.cb.toggle);
if (ImageExpand.on && !this.isHidden) {
return ImageExpand.expand(this);
}
@ -8832,8 +8868,11 @@
if (!(rect.top <= 0 || rect.left <= 0)) {
return;
}
headRect = Header.bar.getBoundingClientRect();
top = rect.top - headRect.top - headRect.height;
top = rect.top;
if (!Conf['Bottom header']) {
headRect = Header.bar.getBoundingClientRect();
top += -headRect.top - headRect.height;
}
root = $.engine === 'webkit' ? d.body : doc;
if (rect.top < 0) {
root.scrollTop += top;
@ -9175,7 +9214,7 @@
expand: function(post) {
var a;
if (post.nodes.longComment) {
if (post.nodes.longComment && !post.nodes.longComment.parentNode) {
$.replace(post.nodes.shortComment, post.nodes.longComment);
post.nodes.comment = post.nodes.longComment;
return;
@ -9445,11 +9484,11 @@
});
Unread.addPosts(posts);
if ((hash = location.hash.match(/\d+/)) && (post = this.posts[hash[0]])) {
post.nodes.root.scrollIntoView();
Header.scrollToPost(post.nodes.root);
} else if (Unread.posts.length) {
$.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView(false);
} else if (posts.length) {
posts[posts.length - 1].nodes.root.scrollIntoView();
Header.scrollToPost(posts[posts.length - 1].nodes.root);
}
$.on(d, 'ThreadUpdate', Unread.onUpdate);
$.on(d, 'scroll visibilitychange', Unread.read);
@ -10070,7 +10109,7 @@
if (Conf['Bottom Scroll']) {
($.engine === 'webkit' ? d.body : doc).scrollTop = d.body.clientHeight;
} else {
nodes[0].scrollIntoView();
Header.scrollToPost(nodes[0]);
}
}
$.queueTask(function() {
@ -10674,6 +10713,9 @@
init: function() {
var board;
if (!Conf['Cooldown']) {
return;
}
board = g.BOARD.ID;
QR.cooldown.types = {
thread: (function() {
@ -10701,6 +10743,9 @@
return $.sync("cooldown." + board, QR.cooldown.sync);
},
start: function() {
if (!Conf['Cooldown']) {
return;
}
if (QR.cooldown.isCounting) {
return;
}
@ -10718,6 +10763,9 @@
set: function(data) {
var cooldown, delay, hasFile, isReply, isSage, post, req, start, type, upSpd;
if (!Conf['Cooldown']) {
return;
}
req = data.req, post = data.post, isReply = data.isReply, delay = data.delay;
start = req ? req.uploadEndTime : Date.now();
if (delay) {

View File

@ -239,9 +239,9 @@ Style =
notEither and _conf['Image Expansion']
notEither and _conf['Image Expansion']
notEither
g.REPLY
g.VIEW is 'thread'
notEither and _conf['Fappe Tyme']
navlinks = ((!g.REPLY and _conf['Index Navigation']) or (g.REPLY and _conf['Reply Navigation'])) and notCatalog
navlinks = ((!g.VIEW is 'thread' and _conf['Index Navigation']) or (g.VIEW is 'thread' and _conf['Reply Navigation'])) and notCatalog
navlinks
]
)
@ -271,19 +271,19 @@ Style =
notCatalog and $ 'body > a[style="cursor: pointer; float: right;"]', d.body
$ '#navtopright .exlinksOptionsLink', d.body
notEither
g.REPLY
g.VIEW is 'thread'
notEither and _conf['Fappe Tyme']
navlinks = ((!g.REPLY and _conf['Index Navigation']) or (g.REPLY and _conf['Reply Navigation'])) and notCatalog
navlinks = ((!g.VIEW is 'thread' and _conf['Index Navigation']) or (g.VIEW is 'thread' and _conf['Reply Navigation'])) and notCatalog
navlinks
]
)
iconOffset = (
if g.REPLY and _conf['Prefetch']
if g.VIEW is 'thread' and _conf['Prefetch']
250 + Style.sidebarOffset.W
else
20 + (
if g.REPLY and _conf['Updater Position'] is 'top'
if g.VIEW is 'thread' and _conf['Updater Position'] is 'top'
100
else
0
@ -996,9 +996,9 @@ MascotTools =
return if el then el.src = "" else null
position = "#{if Conf['Mascot Position'] is 'bottom' or !(Conf['Mascot Position'] is "default" and Conf['Post Form Style'] is "fixed")
0 + (if (!g.REPLY or Conf['Boards Navigation'] is 'sticky bottom') and Conf['4chan SS Navigation'] then 1.6 else 0)
0 + (if (!g.VIEW is 'thread' or Conf['Boards Navigation'] is 'sticky bottom') and Conf['4chan SS Navigation'] then 1.6 else 0)
else
20.3 + (if !g.REPLY or !!$ '#postForm input[name=spoiler]' then 1.4 else 0) + (if Conf['Show Post Form Header'] then 1.5 else 0) + (if Conf['Post Form Decorations'] then 0.2 else 0)
20.3 + (if !g.VIEW is 'thread' or !!$ '#postForm input[name=spoiler]' then 1.4 else 0) + (if Conf['Show Post Form Header'] then 1.5 else 0) + (if Conf['Post Form Decorations'] then 0.2 else 0)
}em"
# If we're editting anything, let's not change mascots any time we change a value.

View File

@ -229,6 +229,10 @@ Config =
true
'Hide the normal post form.'
]
'Cooldown': [
true
'Prevent "flood detected" errors.'
]
'Quote Links':
'Quote Backlinks': [

View File

@ -6,6 +6,9 @@ Header =
id: 'shortcuts'
@hover = $.el 'div',
id: 'hoverUI'
$.on window, 'load hashchange', Header.hashScroll
$.asap (-> d.body), ->
return unless Main.isThisPageLegit()
# Wait for #boardNavMobile instead of #boardNavDesktop,
@ -88,6 +91,17 @@ Header =
custom.hidden = !showBoardList
full.hidden = showBoardList
hashScroll: ->
return unless post = $.id @location.hash[1..]
Header.scrollToPost post
scrollToPost: (post) ->
{top} = post.getBoundingClientRect()
unless Conf['Bottom header']
headRect = Header.bar.getBoundingClientRect()
top += - headRect.top - headRect.height
(if $.engine is 'webkit' then d.body else doc).scrollTop += top
addShortcut: (el) ->
shortcut = $.el 'span',
className: 'shortcut'
@ -1337,8 +1351,11 @@ Keybinds =
location.href = url
hl: (delta, thread) ->
headRect = Header.bar.getBoundingClientRect()
topMargin = headRect.top + headRect.height
if Conf['Bottom header']
topMargin = 0
else
headRect = Header.toggle.getBoundingClientRect()
topMargin = headRect.top + headRect.height
if postEl = $ '.reply.highlight', thread
$.rmClass postEl, 'highlight'
rect = postEl.getBoundingClientRect()
@ -1400,8 +1417,11 @@ Nav =
Nav.scroll +1
getThread: (full) ->
headRect = Header.bar.getBoundingClientRect()
topMargin = headRect.top + headRect.height
if Conf['Bottom header']
topMargin = 0
else
headRect = Header.toggle.getBoundingClientRect()
topMargin = headRect.top + headRect.height
threads = $$ '.thread:not([hidden])'
for thread, i in threads
rect = thread.getBoundingClientRect()
@ -2181,7 +2201,7 @@ QuotePreview =
# Remove the clone that's in the qp from the array.
posts.pop()
for post in posts
$.addClass post.nodes.root, 'qphl'
$.addClass post.nodes.post, 'qphl'
quoterID = $.x('ancestor::*[@id][1]', @).id.match(/\d+$/)[0]
clone = Get.postFromRoot qp.firstChild
@ -2200,7 +2220,7 @@ QuotePreview =
return unless Conf['Quote Highlighting']
for post in [post].concat post.clones
$.rmClass post.nodes.root, 'qphl'
$.rmClass post.nodes.post, 'qphl'
return
QuoteBacklink =
@ -2669,8 +2689,15 @@ ImageExpand =
name: 'Image Expansion'
cb: @node
node: ->
return unless @file and @file.isImage
$.on @file.thumb.parentNode, 'click', ImageExpand.cb.toggle
return unless @file?.isImage
{thumb} = @file
$.on thumb.parentNode, 'click', ImageExpand.cb.toggle
if @isClone and $.hasClass thumb, 'expanding'
# If we clone a post where the image is still loading,
# make it loading in the clone too.
ImageExpand.contract @
ImageExpand.expand @
return
if ImageExpand.on and !@isHidden
ImageExpand.expand @
cb:
@ -2720,8 +2747,10 @@ ImageExpand =
return unless rect.top <= 0 or rect.left <= 0
# Scroll back to the thumbnail when contracting the image
# to avoid being left miles away from the relevant post.
headRect = Header.bar.getBoundingClientRect()
top = rect.top - headRect.top - headRect.height
{top} = rect
unless Conf['Bottom header']
headRect = Header.bar.getBoundingClientRect()
top += - headRect.top - headRect.height
root = if $.engine is 'webkit' then d.body else doc
root.scrollTop += top if rect.top < 0
root.scrollLeft = 0 if rect.left < 0
@ -2944,7 +2973,7 @@ ExpandComment =
post = Get.postFromNode @
ExpandComment.expand post
expand: (post) ->
if post.nodes.longComment
if post.nodes.longComment and !post.nodes.longComment.parentNode
$.replace post.nodes.shortComment, post.nodes.longComment
post.nodes.comment = post.nodes.longComment
return
@ -3130,13 +3159,13 @@ Unread =
defaultValue: 0
Unread.addPosts posts
if (hash = location.hash.match /\d+/) and post = @posts[hash[0]]
post.nodes.root.scrollIntoView()
Header.scrollToPost post.nodes.root
else if Unread.posts.length
# Scroll to before the first unread post.
$.x('preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root).scrollIntoView false
else if posts.length
# Scroll to the last read post.
posts[posts.length - 1].nodes.root.scrollIntoView()
Header.scrollToPost posts[posts.length - 1].nodes.root
$.on d, 'ThreadUpdate', Unread.onUpdate
$.on d, 'scroll visibilitychange', Unread.read
$.on d, 'visibilitychange', Unread.setLine if Conf['Unread Line']
@ -3651,7 +3680,7 @@ ThreadUpdater =
if Conf['Bottom Scroll']
(if $.engine is 'webkit' then d.body else doc).scrollTop = d.body.clientHeight
else
nodes[0].scrollIntoView()
Header.scrollToPost nodes[0]
$.queueTask ->
# Enable 4chan features.

View File

@ -133,6 +133,7 @@ QR =
cooldown:
init: ->
return unless Conf['Cooldown']
board = g.BOARD.ID
QR.cooldown.types =
thread: switch board
@ -149,6 +150,7 @@ QR =
QR.cooldown.start()
$.sync "cooldown.#{board}", QR.cooldown.sync
start: ->
return unless Conf['Cooldown']
return if QR.cooldown.isCounting
QR.cooldown.isCounting = true
QR.cooldown.count()
@ -159,6 +161,7 @@ QR =
QR.cooldown.cooldowns[id] = cooldowns[id]
QR.cooldown.start()
set: (data) ->
return unless Conf['Cooldown']
{req, post, isReply, delay} = data
start = if req then req.uploadEndTime else Date.now()
if delay