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

View File

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

View File

@ -1,99 +1,143 @@
QR.cooldown = QR.cooldown =
seconds: 0
init: -> init: ->
return unless Conf['Cooldown'] return unless Conf['Cooldown']
setTimers = (e) => QR.cooldown.types = e.detail
$.on window, 'cooldown:timers', setTimers # Read cooldown times
$.globalEval 'window.dispatchEvent(new CustomEvent("cooldown:timers", {detail: cooldowns}))' QR.cooldown.delays = if m = Get.scriptData().match /\bcooldowns *= *({[^}]+})/
$.off window, 'cooldown:timers', setTimers JSON.parse m[1]
for type of QR.cooldown.types else
QR.cooldown.types[type] = +QR.cooldown.types[type] {thread: 0, reply: 0, image: 0, reply_intra: 0, image_intra: 0}
key = "cooldown.#{g.BOARD}"
$.get key, {}, (item) -> # The longest reply cooldown, for use in pruning old reply data
QR.cooldown.cooldowns = item[key] 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() QR.cooldown.start()
$.sync key, QR.cooldown.sync $.sync key, QR.cooldown.sync scope for scope, key of keys
start: -> 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.isCounting = true
QR.cooldown.count() QR.cooldown.count()
sync: (cooldowns) -> sync: (scope) -> (cooldowns) ->
# Add each cooldowns, don't overwrite everything in case we QR.cooldown[scope] = cooldowns or {}
# still need to prune one in the current tab to auto-post.
for id of cooldowns
QR.cooldown.cooldowns[id] = cooldowns[id]
QR.cooldown.start() QR.cooldown.start()
set: (data) -> add: (start, threadID, postID) ->
return unless Conf['Cooldown'] return unless Conf['Cooldown']
{req, post, isReply, threadID, delay} = data boardID = g.BOARD.ID
start = if req then req.uploadEndTime else Date.now() QR.cooldown.set 'local', start, {threadID, postID}
if delay QR.cooldown.set 'global', start, {boardID, threadID, postID} if threadID is postID
cooldown = {delay}
else
cooldown = {isReply, threadID}
QR.cooldown.cooldowns[start] = cooldown
$.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns
QR.cooldown.start() QR.cooldown.start()
unset: (id) -> addDelay: (post, delay) ->
delete QR.cooldown.cooldowns[id] return unless Conf['Cooldown']
if Object.keys(QR.cooldown.cooldowns).length cooldown = QR.cooldown.categorize post
$.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns 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 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: -> count: ->
unless Object.keys(QR.cooldown.cooldowns).length now = Date.now()
$.delete "cooldown.#{g.BOARD}" {type, threadID} = QR.cooldown.categorize QR.posts[0]
delete QR.cooldown.isCounting seconds = 0
delete QR.cooldown.seconds
QR.status()
return
clearTimeout QR.cooldown.timeout for scope, key of QR.cooldown.keys
QR.cooldown.timeout = setTimeout QR.cooldown.count, $.SECOND $.forceSync key
save = false
now = Date.now() for start, cooldown of QR.cooldown[scope]
post = QR.posts[0] start = +start
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
elapsed = (now - start) // $.SECOND elapsed = (now - start) // $.SECOND
if elapsed < 0 # clock changed since then? if elapsed < 0 # clock changed since then?
QR.cooldown.unset start delete QR.cooldown[scope][start]
save = true
continue continue
type = unless isReply
'thread' # Explicit delays from error messages
else if hasFile if cooldown.delay?
'image' 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 else
'reply' QR.cooldown.delays[if scope is 'global' then 'thread_global' else 'thread']
maxTimer = Math.max types[type] or 0, types[type + '_intra'] or 0 if maxDelay <= elapsed
unless start <= now <= start + maxTimer * $.SECOND delete QR.cooldown[scope][start]
QR.cooldown.unset start save = true
type += '_intra' if isReply and +post.thread is cooldown.threadID continue
seconds = Math.max seconds, types[type] - elapsed
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. # Update the status when we change posting type.
# Don't get stuck at some random number. # Don't get stuck at some random number.
# Don't interfere with progress status updates. # 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.cooldown.seconds = seconds
QR.status() if update QR.status() if update
QR.submit() if seconds is 0 and QR.cooldown.auto and !QR.req 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. # If we're in a closed thread, #postPassword isn't available.
# And since #delPassword.value is only filled on window.onload # And since #delPassword.value is only filled on window.onload
# we'd rather use #postPassword when we can. # we'd rather use #postPassword when we can.
$.id('delPassword').value $.id('delPassword')?.value or ''
return QR.persona.pwd return QR.persona.pwd
get: (cb) -> get: (cb) ->
@ -68,6 +68,5 @@ QR.persona =
persona = persona =
name: post.name name: post.name
email: if /^sage$/.test post.email then persona.email else post.email 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 flag: post.flag
$.set 'QR.persona', persona $.set 'QR.persona', persona

View File

@ -4,7 +4,7 @@ QR.post = class
className: 'qr-preview' className: 'qr-preview'
draggable: true draggable: true
href: 'javascript:;' 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 = @nodes =
el: el el: el
@ -13,12 +13,6 @@ QR.post = class
spoiler: $ 'input', el spoiler: $ 'input', el
span: el.lastChild 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 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()
@ -72,7 +66,7 @@ QR.post = class
@select() if select @select() if select
@unlock() @unlock()
# Post count temporarily off by 1 when called from QR.post.rm # Post count temporarily off by 1 when called from QR.post.rm
$.queueTask -> QR.captcha.setup() $.queueTask -> QR.captcha.onNewPost()
rm: -> rm: ->
@delete() @delete()
@ -123,6 +117,9 @@ QR.post = class
node.value = @[name] or node.dataset.default or null node.value = @[name] or node.dataset.default or null
QR.tripcodeHider.call QR.nodes['name'] QR.tripcodeHider.call QR.nodes['name']
(if @thread isnt 'new' then $.addClass else $.rmClass) QR.nodes.el, 'reply-to-thread'
@showFileData() @showFileData()
QR.characterCount() QR.characterCount()
@ -134,6 +131,7 @@ QR.post = class
@[name] = input.value or input.dataset.default or null @[name] = input.value or input.dataset.default or null
switch name switch name
when 'thread' when 'thread'
(if @thread isnt 'new' then $.addClass else $.rmClass) QR.nodes.el, 'reply-to-thread'
QR.status() QR.status()
when 'com' when 'com'
@nodes.span.textContent = @com @nodes.span.textContent = @com
@ -271,10 +269,10 @@ QR.post = class
URL.revokeObjectURL @URL URL.revokeObjectURL @URL
updateFilename: -> updateFilename: ->
title = "#{@filename} (#{@filesize})\nCtrl/\u2318+click to edit filename. Shift+click to clear." long = "#{@filename} (#{@filesize})\nCtrl/\u2318+click to edit filename. Shift+click to clear."
@nodes.el.title = title @nodes.el.title = long
return unless @ is QR.selected return unless @ is QR.selected
QR.nodes.fileContainer.title = title QR.nodes.fileContainer.title = long
showFileData: -> showFileData: ->
if @file if @file