The rest of the QR

This commit is contained in:
Zixaphir 2015-01-09 23:31:22 -07:00
parent 13563ffcda
commit e4dde07c11
5 changed files with 440 additions and 271 deletions

View File

@ -10053,119 +10053,183 @@
};
QR.cooldown = {
seconds: 0,
init: function() {
var key, setTimers, type;
var delay, items, key, keys, m, scope, type, _ref, _results;
if (!Conf['Cooldown']) {
return;
}
setTimers = (function(_this) {
return function(e) {
return QR.cooldown.types = e.detail;
};
})(this);
$.on(window, 'cooldown:timers', setTimers);
$.globalEval('window.dispatchEvent(new CustomEvent("cooldown:timers", {detail: cooldowns}))');
$.off(window, 'cooldown:timers', setTimers);
for (type in QR.cooldown.types) {
QR.cooldown.types[type] = +QR.cooldown.types[type];
QR.cooldown.delays = (m = Get.scriptData().match(/\bcooldowns *= *({[^}]+})/)) ? JSON.parse(m[1]) : {
thread: 0,
reply: 0,
image: 0,
reply_intra: 0,
image_intra: 0
};
QR.cooldown.maxDelay = 0;
_ref = QR.cooldown.delays;
for (type in _ref) {
delay = _ref[type];
if (type !== 'thread') {
QR.cooldown.maxDelay = Math.max(QR.cooldown.maxDelay, delay);
}
}
key = "cooldown." + g.BOARD;
$.get(key, {}, function(item) {
QR.cooldown.cooldowns = item[key];
QR.cooldown.delays['thread_global'] = 300;
keys = QR.cooldown.keys = {
local: "cooldown." + g.BOARD,
global: 'cooldown.global'
};
items = {};
for (scope in keys) {
key = keys[scope];
items[key] = {};
}
$.get(items, function(items) {
for (scope in keys) {
key = keys[scope];
QR.cooldown[scope] = items[key];
}
return QR.cooldown.start();
});
return $.sync(key, QR.cooldown.sync);
_results = [];
for (scope in keys) {
key = keys[scope];
_results.push($.sync(key, QR.cooldown.sync(scope)));
}
return _results;
},
start: function() {
if (QR.cooldown.isCounting || !Object.keys(QR.cooldown.cooldowns).length) {
if (QR.cooldown.isCounting || Object.keys(QR.cooldown.local).length + Object.keys(QR.cooldown.global).length === 0) {
return;
}
QR.cooldown.isCounting = true;
return QR.cooldown.count();
},
sync: function(cooldowns) {
var id;
for (id in cooldowns) {
QR.cooldown.cooldowns[id] = cooldowns[id];
}
return QR.cooldown.start();
sync: function(scope) {
return function(cooldowns) {
QR.cooldown[scope] = cooldowns || {};
return QR.cooldown.start();
};
},
set: function(data) {
var cooldown, delay, isReply, post, req, start, threadID;
add: function(start, threadID, postID) {
var boardID;
if (!Conf['Cooldown']) {
return;
}
req = data.req, post = data.post, isReply = data.isReply, threadID = data.threadID, delay = data.delay;
start = req ? req.uploadEndTime : Date.now();
if (delay) {
cooldown = {
delay: delay
};
} else {
cooldown = {
isReply: isReply,
threadID: threadID
};
boardID = g.BOARD.ID;
QR.cooldown.set('local', start, {
threadID: threadID,
postID: postID
});
if (threadID === postID) {
QR.cooldown.set('global', start, {
boardID: boardID,
threadID: threadID,
postID: postID
});
}
QR.cooldown.cooldowns[start] = cooldown;
$.set("cooldown." + g.BOARD, QR.cooldown.cooldowns);
return QR.cooldown.start();
},
unset: function(id) {
delete QR.cooldown.cooldowns[id];
if (Object.keys(QR.cooldown.cooldowns).length) {
return $.set("cooldown." + g.BOARD, QR.cooldown.cooldowns);
addDelay: function(post, delay) {
var cooldown;
if (!Conf['Cooldown']) {
return;
}
cooldown = QR.cooldown.categorize(post);
cooldown.delay = delay;
QR.cooldown.set('local', Date.now(), cooldown);
return QR.cooldown.start();
},
"delete": function(post) {
var cooldown, id, _ref;
if (!(Conf['Cooldown'] && g.BOARD.ID === post.board.ID)) {
return;
}
$.forceSync(QR.cooldown.keys.local);
_ref = QR.cooldown.local;
for (id in _ref) {
cooldown = _ref[id];
if ((cooldown.delay == null) && cooldown.threadID === post.thread.ID && cooldown.postID === post.ID) {
delete QR.cooldown.local[id];
}
}
return QR.cooldown.save('local');
},
categorize: function(post) {
if (post.thread === 'new') {
return {
type: 'thread'
};
} else {
return $["delete"]("cooldown." + g.BOARD);
return {
type: !!post.file ? 'image' : 'reply',
threadID: +post.thread
};
}
},
set: function(scope, id, value) {
$.forceSync(QR.cooldown.keys[scope]);
QR.cooldown[scope][id] = value;
return $.set(QR.cooldown.keys[scope], QR.cooldown[scope]);
},
save: function(scope) {
if (Object.keys(QR.cooldown[scope]).length) {
return $.set(QR.cooldown.keys[scope], QR.cooldown[scope]);
} else {
return $["delete"](QR.cooldown.keys[scope]);
}
},
count: function() {
var cooldown, cooldowns, elapsed, hasFile, isReply, maxTimer, now, post, seconds, start, type, types, update, _ref;
if (!Object.keys(QR.cooldown.cooldowns).length) {
$["delete"]("cooldown." + g.BOARD);
delete QR.cooldown.isCounting;
delete QR.cooldown.seconds;
QR.status();
return;
}
clearTimeout(QR.cooldown.timeout);
QR.cooldown.timeout = setTimeout(QR.cooldown.count, $.SECOND);
var cooldown, elapsed, key, maxDelay, now, save, scope, seconds, start, suffix, threadID, type, update, _ref, _ref1, _ref2;
now = Date.now();
post = QR.posts[0];
isReply = post.thread !== 'new';
hasFile = !!post.file;
seconds = null;
_ref = QR.cooldown, types = _ref.types, cooldowns = _ref.cooldowns;
for (start in cooldowns) {
cooldown = cooldowns[start];
start = +start;
if ('delay' in cooldown) {
if (cooldown.delay) {
seconds = Math.max(seconds, cooldown.delay--);
} else {
seconds = Math.max(seconds, 0);
QR.cooldown.unset(start);
}
continue;
}
if (isReply === cooldown.isReply) {
_ref = QR.cooldown.categorize(QR.posts[0]), type = _ref.type, threadID = _ref.threadID;
seconds = 0;
_ref1 = QR.cooldown.keys;
for (scope in _ref1) {
key = _ref1[scope];
$.forceSync(key);
save = false;
_ref2 = QR.cooldown[scope];
for (start in _ref2) {
cooldown = _ref2[start];
start = +start;
elapsed = Math.floor((now - start) / $.SECOND);
if (elapsed < 0) {
QR.cooldown.unset(start);
delete QR.cooldown[scope][start];
save = true;
continue;
}
type = !isReply ? 'thread' : hasFile ? 'image' : 'reply';
maxTimer = Math.max(types[type] || 0, types[type + '_intra'] || 0);
if (!((start <= now && now <= start + maxTimer * $.SECOND))) {
QR.cooldown.unset(start);
if (cooldown.delay != null) {
if (cooldown.delay <= elapsed) {
delete QR.cooldown[scope][start];
save = true;
} else if (cooldown.type === type && cooldown.threadID === threadID) {
seconds = Math.max(seconds, cooldown.delay - elapsed);
}
continue;
}
if (isReply && +post.thread === cooldown.threadID) {
type += '_intra';
maxDelay = cooldown.threadID !== cooldown.postID ? QR.cooldown.maxDelay : QR.cooldown.delays[scope === 'global' ? 'thread_global' : 'thread'];
if (maxDelay <= elapsed) {
delete QR.cooldown[scope][start];
save = true;
continue;
}
seconds = Math.max(seconds, types[type] - elapsed);
if ((type === 'thread') === (cooldown.threadID === cooldown.postID)) {
suffix = scope === 'global' ? '_global' : type !== 'thread' && threadID === cooldown.threadID ? '_intra' : '';
seconds = Math.max(seconds, QR.cooldown.delays[type + suffix] - elapsed);
}
}
if (save) {
QR.cooldown.save(scope);
}
}
update = seconds !== null || !!QR.cooldown.seconds;
if (Object.keys(QR.cooldown.local).length + Object.keys(QR.cooldown.global).length) {
clearTimeout(QR.cooldown.timeout);
QR.cooldown.timeout = setTimeout(QR.cooldown.count, $.SECOND);
} else {
delete QR.cooldown.isCounting;
}
update = seconds !== QR.cooldown.seconds;
QR.cooldown.seconds = seconds;
if (update) {
QR.status();
@ -10244,9 +10308,9 @@
}
},
getPassword: function() {
var input, m;
var input, m, _ref;
if (!QR.persona.pwd) {
QR.persona.pwd = (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : (input = $.id('postPassword')) ? input.value : $.id('delPassword').value;
QR.persona.pwd = (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : (input = $.id('postPassword')) ? input.value : ((_ref = $.id('delPassword')) != null ? _ref.value : void 0) || '';
}
return QR.persona.pwd;
},
@ -10264,7 +10328,6 @@
persona = {
name: post.name,
email: /^sage$/.test(post.email) ? persona.email : post.email,
sub: Conf['Remember Subject'] ? post.sub : void 0,
flag: post.flag
};
return $.set('QR.persona', persona);
@ -10275,12 +10338,14 @@
QR.post = (function() {
function _Class(select) {
this.select = __bind(this.select, this);
var el, elm, event, prev, _i, _j, _len, _len1, _ref, _ref1;
var el, event, prev, _i, _len, _ref;
el = $.el('a', {
className: 'qr-preview',
draggable: true,
href: 'javascript:;',
innerHTML: '<a class="remove fa" title=Remove>\uf057</a><label hidden><input type=checkbox> Spoiler</label><span></span>'
href: 'javascript:;'
});
$.extend(el, {
innerHTML: "<a class=\"remove fa\" title=\"Remove\"></a><label hidden><input type=\"checkbox\"> Spoiler</label><span></span>"
});
this.nodes = {
el: el,
@ -10289,12 +10354,6 @@
spoiler: $('input', el),
span: el.lastChild
};
_ref = $$('*', el);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
elm = _ref[_i];
$.on(elm, 'blur', QR.focusout);
$.on(elm, 'focus', QR.focusin);
}
$.on(el, 'click', this.select);
$.on(this.nodes.rm, 'click', (function(_this) {
return function(e) {
@ -10316,9 +10375,9 @@
};
})(this));
$.add(QR.nodes.dumpList, el);
_ref1 = ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop'];
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
event = _ref1[_j];
_ref = ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
event = _ref[_i];
$.on(el, event.toLowerCase(), this[event]);
}
this.thread = g.VIEW === 'thread' ? g.THREADID : 'new';
@ -10343,7 +10402,7 @@
}
this.unlock();
$.queueTask(function() {
return QR.captcha.setup();
return QR.captcha.onNewPost();
});
}
@ -10418,6 +10477,7 @@
node.value = this[name] || node.dataset["default"] || null;
}
QR.tripcodeHider.call(QR.nodes['name']);
(this.thread !== 'new' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread');
this.showFileData();
return QR.characterCount();
};
@ -10432,6 +10492,7 @@
this[name] = input.value || input.dataset["default"] || null;
switch (name) {
case 'thread':
(this.thread !== 'new' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread');
return QR.status();
case 'com':
this.nodes.span.textContent = this.com;
@ -10598,13 +10659,13 @@
};
_Class.prototype.updateFilename = function() {
var title;
title = "" + this.filename + " (" + this.filesize + ")\nCtrl/\u2318+click to edit filename. Shift+click to clear.";
this.nodes.el.title = title;
var long;
long = "" + this.filename + " (" + this.filesize + ")\nCtrl/\u2318+click to edit filename. Shift+click to clear.";
this.nodes.el.title = long;
if (this !== QR.selected) {
return;
}
return QR.nodes.fileContainer.title = title;
return QR.nodes.fileContainer.title = long;
};
_Class.prototype.showFileData = function() {

View File

@ -10096,119 +10096,183 @@
};
QR.cooldown = {
seconds: 0,
init: function() {
var key, setTimers, type;
var delay, items, key, keys, m, scope, type, _ref, _results;
if (!Conf['Cooldown']) {
return;
}
setTimers = (function(_this) {
return function(e) {
return QR.cooldown.types = e.detail;
};
})(this);
$.on(window, 'cooldown:timers', setTimers);
$.globalEval('window.dispatchEvent(new CustomEvent("cooldown:timers", {detail: cooldowns}))');
$.off(window, 'cooldown:timers', setTimers);
for (type in QR.cooldown.types) {
QR.cooldown.types[type] = +QR.cooldown.types[type];
QR.cooldown.delays = (m = Get.scriptData().match(/\bcooldowns *= *({[^}]+})/)) ? JSON.parse(m[1]) : {
thread: 0,
reply: 0,
image: 0,
reply_intra: 0,
image_intra: 0
};
QR.cooldown.maxDelay = 0;
_ref = QR.cooldown.delays;
for (type in _ref) {
delay = _ref[type];
if (type !== 'thread') {
QR.cooldown.maxDelay = Math.max(QR.cooldown.maxDelay, delay);
}
}
key = "cooldown." + g.BOARD;
$.get(key, {}, function(item) {
QR.cooldown.cooldowns = item[key];
QR.cooldown.delays['thread_global'] = 300;
keys = QR.cooldown.keys = {
local: "cooldown." + g.BOARD,
global: 'cooldown.global'
};
items = {};
for (scope in keys) {
key = keys[scope];
items[key] = {};
}
$.get(items, function(items) {
for (scope in keys) {
key = keys[scope];
QR.cooldown[scope] = items[key];
}
return QR.cooldown.start();
});
return $.sync(key, QR.cooldown.sync);
_results = [];
for (scope in keys) {
key = keys[scope];
_results.push($.sync(key, QR.cooldown.sync(scope)));
}
return _results;
},
start: function() {
if (QR.cooldown.isCounting || !Object.keys(QR.cooldown.cooldowns).length) {
if (QR.cooldown.isCounting || Object.keys(QR.cooldown.local).length + Object.keys(QR.cooldown.global).length === 0) {
return;
}
QR.cooldown.isCounting = true;
return QR.cooldown.count();
},
sync: function(cooldowns) {
var id;
for (id in cooldowns) {
QR.cooldown.cooldowns[id] = cooldowns[id];
}
return QR.cooldown.start();
sync: function(scope) {
return function(cooldowns) {
QR.cooldown[scope] = cooldowns || {};
return QR.cooldown.start();
};
},
set: function(data) {
var cooldown, delay, isReply, post, req, start, threadID;
add: function(start, threadID, postID) {
var boardID;
if (!Conf['Cooldown']) {
return;
}
req = data.req, post = data.post, isReply = data.isReply, threadID = data.threadID, delay = data.delay;
start = req ? req.uploadEndTime : Date.now();
if (delay) {
cooldown = {
delay: delay
};
} else {
cooldown = {
isReply: isReply,
threadID: threadID
};
boardID = g.BOARD.ID;
QR.cooldown.set('local', start, {
threadID: threadID,
postID: postID
});
if (threadID === postID) {
QR.cooldown.set('global', start, {
boardID: boardID,
threadID: threadID,
postID: postID
});
}
QR.cooldown.cooldowns[start] = cooldown;
$.set("cooldown." + g.BOARD, QR.cooldown.cooldowns);
return QR.cooldown.start();
},
unset: function(id) {
delete QR.cooldown.cooldowns[id];
if (Object.keys(QR.cooldown.cooldowns).length) {
return $.set("cooldown." + g.BOARD, QR.cooldown.cooldowns);
addDelay: function(post, delay) {
var cooldown;
if (!Conf['Cooldown']) {
return;
}
cooldown = QR.cooldown.categorize(post);
cooldown.delay = delay;
QR.cooldown.set('local', Date.now(), cooldown);
return QR.cooldown.start();
},
"delete": function(post) {
var cooldown, id, _ref;
if (!(Conf['Cooldown'] && g.BOARD.ID === post.board.ID)) {
return;
}
$.forceSync(QR.cooldown.keys.local);
_ref = QR.cooldown.local;
for (id in _ref) {
cooldown = _ref[id];
if ((cooldown.delay == null) && cooldown.threadID === post.thread.ID && cooldown.postID === post.ID) {
delete QR.cooldown.local[id];
}
}
return QR.cooldown.save('local');
},
categorize: function(post) {
if (post.thread === 'new') {
return {
type: 'thread'
};
} else {
return $["delete"]("cooldown." + g.BOARD);
return {
type: !!post.file ? 'image' : 'reply',
threadID: +post.thread
};
}
},
set: function(scope, id, value) {
$.forceSync(QR.cooldown.keys[scope]);
QR.cooldown[scope][id] = value;
return $.set(QR.cooldown.keys[scope], QR.cooldown[scope]);
},
save: function(scope) {
if (Object.keys(QR.cooldown[scope]).length) {
return $.set(QR.cooldown.keys[scope], QR.cooldown[scope]);
} else {
return $["delete"](QR.cooldown.keys[scope]);
}
},
count: function() {
var cooldown, cooldowns, elapsed, hasFile, isReply, maxTimer, now, post, seconds, start, type, types, update, _ref;
if (!Object.keys(QR.cooldown.cooldowns).length) {
$["delete"]("cooldown." + g.BOARD);
delete QR.cooldown.isCounting;
delete QR.cooldown.seconds;
QR.status();
return;
}
clearTimeout(QR.cooldown.timeout);
QR.cooldown.timeout = setTimeout(QR.cooldown.count, $.SECOND);
var cooldown, elapsed, key, maxDelay, now, save, scope, seconds, start, suffix, threadID, type, update, _ref, _ref1, _ref2;
now = Date.now();
post = QR.posts[0];
isReply = post.thread !== 'new';
hasFile = !!post.file;
seconds = null;
_ref = QR.cooldown, types = _ref.types, cooldowns = _ref.cooldowns;
for (start in cooldowns) {
cooldown = cooldowns[start];
start = +start;
if ('delay' in cooldown) {
if (cooldown.delay) {
seconds = Math.max(seconds, cooldown.delay--);
} else {
seconds = Math.max(seconds, 0);
QR.cooldown.unset(start);
}
continue;
}
if (isReply === cooldown.isReply) {
_ref = QR.cooldown.categorize(QR.posts[0]), type = _ref.type, threadID = _ref.threadID;
seconds = 0;
_ref1 = QR.cooldown.keys;
for (scope in _ref1) {
key = _ref1[scope];
$.forceSync(key);
save = false;
_ref2 = QR.cooldown[scope];
for (start in _ref2) {
cooldown = _ref2[start];
start = +start;
elapsed = Math.floor((now - start) / $.SECOND);
if (elapsed < 0) {
QR.cooldown.unset(start);
delete QR.cooldown[scope][start];
save = true;
continue;
}
type = !isReply ? 'thread' : hasFile ? 'image' : 'reply';
maxTimer = Math.max(types[type] || 0, types[type + '_intra'] || 0);
if (!((start <= now && now <= start + maxTimer * $.SECOND))) {
QR.cooldown.unset(start);
if (cooldown.delay != null) {
if (cooldown.delay <= elapsed) {
delete QR.cooldown[scope][start];
save = true;
} else if (cooldown.type === type && cooldown.threadID === threadID) {
seconds = Math.max(seconds, cooldown.delay - elapsed);
}
continue;
}
if (isReply && +post.thread === cooldown.threadID) {
type += '_intra';
maxDelay = cooldown.threadID !== cooldown.postID ? QR.cooldown.maxDelay : QR.cooldown.delays[scope === 'global' ? 'thread_global' : 'thread'];
if (maxDelay <= elapsed) {
delete QR.cooldown[scope][start];
save = true;
continue;
}
seconds = Math.max(seconds, types[type] - elapsed);
if ((type === 'thread') === (cooldown.threadID === cooldown.postID)) {
suffix = scope === 'global' ? '_global' : type !== 'thread' && threadID === cooldown.threadID ? '_intra' : '';
seconds = Math.max(seconds, QR.cooldown.delays[type + suffix] - elapsed);
}
}
if (save) {
QR.cooldown.save(scope);
}
}
update = seconds !== null || !!QR.cooldown.seconds;
if (Object.keys(QR.cooldown.local).length + Object.keys(QR.cooldown.global).length) {
clearTimeout(QR.cooldown.timeout);
QR.cooldown.timeout = setTimeout(QR.cooldown.count, $.SECOND);
} else {
delete QR.cooldown.isCounting;
}
update = seconds !== QR.cooldown.seconds;
QR.cooldown.seconds = seconds;
if (update) {
QR.status();
@ -10287,9 +10351,9 @@
}
},
getPassword: function() {
var input, m;
var input, m, _ref;
if (!QR.persona.pwd) {
QR.persona.pwd = (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : (input = $.id('postPassword')) ? input.value : $.id('delPassword').value;
QR.persona.pwd = (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : (input = $.id('postPassword')) ? input.value : ((_ref = $.id('delPassword')) != null ? _ref.value : void 0) || '';
}
return QR.persona.pwd;
},
@ -10307,7 +10371,6 @@
persona = {
name: post.name,
email: /^sage$/.test(post.email) ? persona.email : post.email,
sub: Conf['Remember Subject'] ? post.sub : void 0,
flag: post.flag
};
return $.set('QR.persona', persona);
@ -10322,8 +10385,10 @@
el = $.el('a', {
className: 'qr-preview',
draggable: true,
href: 'javascript:;',
innerHTML: '<a class="remove fa" title=Remove>\uf057</a><label hidden><input type=checkbox> Spoiler</label><span></span>'
href: 'javascript:;'
});
$.extend(el, {
innerHTML: "<a class=\"remove fa\" title=\"Remove\"></a><label hidden><input type=\"checkbox\"> Spoiler</label><span></span>"
});
this.nodes = {
el: el,
@ -10380,7 +10445,7 @@
}
this.unlock();
$.queueTask(function() {
return QR.captcha.setup();
return QR.captcha.onNewPost();
});
}
@ -10455,6 +10520,7 @@
node.value = this[name] || node.dataset["default"] || null;
}
QR.tripcodeHider.call(QR.nodes['name']);
(this.thread !== 'new' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread');
this.showFileData();
return QR.characterCount();
};
@ -10469,6 +10535,7 @@
this[name] = input.value || input.dataset["default"] || null;
switch (name) {
case 'thread':
(this.thread !== 'new' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread');
return QR.status();
case 'com':
this.nodes.span.textContent = this.com;
@ -10632,13 +10699,13 @@
};
_Class.prototype.updateFilename = function() {
var title;
title = "" + this.filename + " (" + this.filesize + ")\nCtrl/\u2318+click to edit filename. Shift+click to clear.";
this.nodes.el.title = title;
var long;
long = "" + this.filename + " (" + this.filesize + ")\nCtrl/\u2318+click to edit filename. Shift+click to clear.";
this.nodes.el.title = long;
if (this !== QR.selected) {
return;
}
return QR.nodes.fileContainer.title = title;
return QR.nodes.fileContainer.title = long;
};
_Class.prototype.showFileData = function() {

View File

@ -1,99 +1,143 @@
QR.cooldown =
seconds: 0
init: ->
return unless Conf['Cooldown']
setTimers = (e) => QR.cooldown.types = e.detail
$.on window, 'cooldown:timers', setTimers
$.globalEval 'window.dispatchEvent(new CustomEvent("cooldown:timers", {detail: cooldowns}))'
$.off window, 'cooldown:timers', setTimers
for type of QR.cooldown.types
QR.cooldown.types[type] = +QR.cooldown.types[type]
key = "cooldown.#{g.BOARD}"
$.get key, {}, (item) ->
QR.cooldown.cooldowns = item[key]
# Read cooldown times
QR.cooldown.delays = if m = Get.scriptData().match /\bcooldowns *= *({[^}]+})/
JSON.parse m[1]
else
{thread: 0, reply: 0, image: 0, reply_intra: 0, image_intra: 0}
# The longest reply cooldown, for use in pruning old reply data
QR.cooldown.maxDelay = 0
for type, delay of QR.cooldown.delays when type isnt 'thread'
QR.cooldown.maxDelay = Math.max QR.cooldown.maxDelay, delay
# There is a 300 second global thread cooldown.
QR.cooldown.delays['thread_global'] = 300
# Retrieve recent posts and delays.
keys = QR.cooldown.keys =
local: "cooldown.#{g.BOARD}"
global: 'cooldown.global'
items = {}
items[key] = {} for scope, key of keys
$.get items, (items) ->
QR.cooldown[scope] = items[key] for scope, key of keys
QR.cooldown.start()
$.sync key, QR.cooldown.sync
$.sync key, QR.cooldown.sync scope for scope, key of keys
start: ->
return if QR.cooldown.isCounting or !Object.keys(QR.cooldown.cooldowns).length
return if QR.cooldown.isCounting or Object.keys(QR.cooldown.local).length + Object.keys(QR.cooldown.global).length is 0
QR.cooldown.isCounting = true
QR.cooldown.count()
sync: (cooldowns) ->
# Add each cooldowns, don't overwrite everything in case we
# still need to prune one in the current tab to auto-post.
for id of cooldowns
QR.cooldown.cooldowns[id] = cooldowns[id]
sync: (scope) -> (cooldowns) ->
QR.cooldown[scope] = cooldowns or {}
QR.cooldown.start()
set: (data) ->
add: (start, threadID, postID) ->
return unless Conf['Cooldown']
{req, post, isReply, threadID, delay} = data
start = if req then req.uploadEndTime else Date.now()
if delay
cooldown = {delay}
else
cooldown = {isReply, threadID}
QR.cooldown.cooldowns[start] = cooldown
$.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns
boardID = g.BOARD.ID
QR.cooldown.set 'local', start, {threadID, postID}
QR.cooldown.set 'global', start, {boardID, threadID, postID} if threadID is postID
QR.cooldown.start()
unset: (id) ->
delete QR.cooldown.cooldowns[id]
if Object.keys(QR.cooldown.cooldowns).length
$.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns
addDelay: (post, delay) ->
return unless Conf['Cooldown']
cooldown = QR.cooldown.categorize post
cooldown.delay = delay
QR.cooldown.set 'local', Date.now(), cooldown
QR.cooldown.start()
delete: (post) ->
return unless Conf['Cooldown'] and g.BOARD.ID is post.board.ID
$.forceSync QR.cooldown.keys.local
for id, cooldown of QR.cooldown.local
if !cooldown.delay? and cooldown.threadID is post.thread.ID and cooldown.postID is post.ID
delete QR.cooldown.local[id]
QR.cooldown.save 'local'
categorize: (post) ->
if post.thread is 'new'
type: 'thread'
else
$.delete "cooldown.#{g.BOARD}"
type: if !!post.file then 'image' else 'reply'
threadID: +post.thread
set: (scope, id, value) ->
$.forceSync QR.cooldown.keys[scope]
QR.cooldown[scope][id] = value
$.set QR.cooldown.keys[scope], QR.cooldown[scope]
save: (scope) ->
if Object.keys(QR.cooldown[scope]).length
$.set QR.cooldown.keys[scope], QR.cooldown[scope]
else
$.delete QR.cooldown.keys[scope]
count: ->
unless Object.keys(QR.cooldown.cooldowns).length
$.delete "cooldown.#{g.BOARD}"
delete QR.cooldown.isCounting
delete QR.cooldown.seconds
QR.status()
return
now = Date.now()
{type, threadID} = QR.cooldown.categorize QR.posts[0]
seconds = 0
clearTimeout QR.cooldown.timeout
QR.cooldown.timeout = setTimeout QR.cooldown.count, $.SECOND
for scope, key of QR.cooldown.keys
$.forceSync key
save = false
now = Date.now()
post = QR.posts[0]
isReply = post.thread isnt 'new'
hasFile = !!post.file
seconds = null
{types, cooldowns} = QR.cooldown
for start, cooldown of cooldowns
start = +start
if 'delay' of cooldown
if cooldown.delay
seconds = Math.max seconds, cooldown.delay--
else
seconds = Math.max seconds, 0
QR.cooldown.unset start
continue
if isReply is cooldown.isReply
# Only cooldowns relevant to this post can set the seconds variable:
# reply cooldown with a reply, thread cooldown with a thread
for start, cooldown of QR.cooldown[scope]
start = +start
elapsed = (now - start) // $.SECOND
if elapsed < 0 # clock changed since then?
QR.cooldown.unset start
delete QR.cooldown[scope][start]
save = true
continue
type = unless isReply
'thread'
else if hasFile
'image'
# Explicit delays from error messages
if cooldown.delay?
if cooldown.delay <= elapsed
delete QR.cooldown[scope][start]
save = true
else if cooldown.type is type and cooldown.threadID is threadID
# Delays only apply to the given post type and thread.
seconds = Math.max seconds, cooldown.delay - elapsed
continue
# Clean up expired cooldowns
maxDelay = if cooldown.threadID isnt cooldown.postID
QR.cooldown.maxDelay
else
'reply'
maxTimer = Math.max types[type] or 0, types[type + '_intra'] or 0
unless start <= now <= start + maxTimer * $.SECOND
QR.cooldown.unset start
type += '_intra' if isReply and +post.thread is cooldown.threadID
seconds = Math.max seconds, types[type] - elapsed
QR.cooldown.delays[if scope is 'global' then 'thread_global' else 'thread']
if maxDelay <= elapsed
delete QR.cooldown[scope][start]
save = true
continue
if (type is 'thread') is (cooldown.threadID is cooldown.postID)
# Only cooldowns relevant to this post can set the seconds variable:
# reply cooldown with a reply, thread cooldown with a thread
suffix = if scope is 'global'
'_global'
else if type isnt 'thread' and threadID is cooldown.threadID
'_intra'
else
''
seconds = Math.max seconds, QR.cooldown.delays[type + suffix] - elapsed
QR.cooldown.save scope if save
if Object.keys(QR.cooldown.local).length + Object.keys(QR.cooldown.global).length
clearTimeout QR.cooldown.timeout
QR.cooldown.timeout = setTimeout QR.cooldown.count, $.SECOND
else
delete QR.cooldown.isCounting
# Update the status when we change posting type.
# Don't get stuck at some random number.
# Don't interfere with progress status updates.
update = seconds isnt null or !!QR.cooldown.seconds
update = seconds isnt QR.cooldown.seconds
QR.cooldown.seconds = seconds
QR.status() if update
QR.submit() if seconds is 0 and QR.cooldown.auto and !QR.req

View File

@ -56,7 +56,7 @@ QR.persona =
# If we're in a closed thread, #postPassword isn't available.
# And since #delPassword.value is only filled on window.onload
# we'd rather use #postPassword when we can.
$.id('delPassword').value
$.id('delPassword')?.value or ''
return QR.persona.pwd
get: (cb) ->
@ -68,6 +68,5 @@ QR.persona =
persona =
name: post.name
email: if /^sage$/.test post.email then persona.email else post.email
sub: if Conf['Remember Subject'] then post.sub else undefined
flag: post.flag
$.set 'QR.persona', persona

View File

@ -4,7 +4,7 @@ QR.post = class
className: 'qr-preview'
draggable: true
href: 'javascript:;'
innerHTML: '<a class="remove fa" title=Remove>\uf057</a><label hidden><input type=checkbox> Spoiler</label><span></span>'
$.extend el, <%= html('<a class="remove fa" title="Remove">\uf057</a><label hidden><input type="checkbox"> Spoiler</label><span></span>') %>
@nodes =
el: el
@ -13,12 +13,6 @@ QR.post = class
spoiler: $ 'input', el
span: el.lastChild
<% if (type === 'userscript') { %>
# XXX Firefox lacks focusin/focusout support.
for elm in $$ '*', el
$.on elm, 'blur', QR.focusout
$.on elm, 'focus', QR.focusin
<% } %>
$.on el, 'click', @select
$.on @nodes.rm, 'click', (e) => e.stopPropagation(); @rm()
$.on @nodes.label, 'click', (e) => e.stopPropagation()
@ -72,7 +66,7 @@ QR.post = class
@select() if select
@unlock()
# Post count temporarily off by 1 when called from QR.post.rm
$.queueTask -> QR.captcha.setup()
$.queueTask -> QR.captcha.onNewPost()
rm: ->
@delete()
@ -123,6 +117,9 @@ QR.post = class
node.value = @[name] or node.dataset.default or null
QR.tripcodeHider.call QR.nodes['name']
(if @thread isnt 'new' then $.addClass else $.rmClass) QR.nodes.el, 'reply-to-thread'
@showFileData()
QR.characterCount()
@ -134,6 +131,7 @@ QR.post = class
@[name] = input.value or input.dataset.default or null
switch name
when 'thread'
(if @thread isnt 'new' then $.addClass else $.rmClass) QR.nodes.el, 'reply-to-thread'
QR.status()
when 'com'
@nodes.span.textContent = @com
@ -271,10 +269,10 @@ QR.post = class
URL.revokeObjectURL @URL
updateFilename: ->
title = "#{@filename} (#{@filesize})\nCtrl/\u2318+click to edit filename. Shift+click to clear."
@nodes.el.title = title
long = "#{@filename} (#{@filesize})\nCtrl/\u2318+click to edit filename. Shift+click to clear."
@nodes.el.title = long
return unless @ is QR.selected
QR.nodes.fileContainer.title = title
QR.nodes.fileContainer.title = long
showFileData: ->
if @file