diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e2d8a948..c9189a341 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ +**MayhemYDG**: + - Tiny posting cooldown adjustment: + * You can post an image reply immediately after a non-image reply. + - Update posting cooldown timers to match 4chan settings: + * Cooldown may vary between inter-thread and intra-thread replies. + * Cooldown may vary when posting a file or not. + * Cooldown does not take sageing into account anymore. + * Timers vary across boards. + - Updated post and deletion cooldown timers to match 4chan changes: they are now twice longer. + - Added support for the flag selector on /pol/. + ### v1.2.39 *2013-09-19* - **seaweedchan**: - Fix thread updater bug introduced in last version diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 2b2c0bfa9..cfdff4a90 100755 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -1,21 +1,14 @@ module.exports = (grunt) -> - concatOptions = - process: Object.create(null, data: - get: -> grunt.config 'pkg' - enumerable: true - ) - shellOptions = - stdout: true - stderr: true - failOnError: true - # Project configuration. grunt.initConfig pkg: grunt.file.readJSON 'package.json' concat: + options: process: Object.create(null, data: + get: -> grunt.config 'pkg' + enumerable: true + ) coffee: - options: concatOptions src: [ 'src/General/Config.coffee' 'src/General/Globals.coffee' @@ -41,13 +34,11 @@ module.exports = (grunt) -> dest: 'tmp-<%= pkg.type %>/script.coffee' meta: - options: concatOptions files: 'LICENSE': 'src/General/meta/banner.js', 'latest.js': 'src/General/meta/latest.js' crx: - options: concatOptions files: 'builds/crx/manifest.json': 'src/General/meta/manifest.json' 'builds/crx/script.js': [ @@ -57,7 +48,6 @@ module.exports = (grunt) -> 'tmp-<%= pkg.type %>/script.js' ] userscript: - options: concatOptions files: 'builds/<%= pkg.name %>.meta.js': 'src/General/meta/metadata.js' 'builds/<%= pkg.name %>.user.js': [ @@ -96,22 +86,23 @@ module.exports = (grunt) -> push: false shell: + options: + stdout: true + stderr: true + failOnError: true commit: - options: shellOptions command: [ 'git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>."' 'git tag -a <%= pkg.version %> -m "<%= pkg.meta.name %> v<%= pkg.version %>."' 'git tag -af stable -m "<%= pkg.meta.name %> v<%= pkg.version %>."' ].join ' && ' - push: - options: shellOptions command: 'git push origin --tags -f && git push origin --all' watch: + options: + interrupt: true all: - options: - interrupt: true files: [ 'Gruntfile.coffee' 'package.json' diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index dba306191..f1715c195 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -5254,7 +5254,8 @@ persona = { name: post.name, email: /^sage$/.test(post.email) ? persona.email : post.email, - sub: Conf['Remember Subject'] ? post.sub : void 0 + sub: Conf['Remember Subject'] ? post.sub : void 0, + flag: post.flag }; return $.set('QR.persona', persona); }); @@ -5318,12 +5319,11 @@ delay: delay }; } else { - if (post.file) { + if (hasFile = !!post.file) { upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND); QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2; QR.cooldown.upSpd = upSpd; } - hasFile = !!post.file; cooldown = { isReply: isReply, hasFile: hasFile, @@ -5380,7 +5380,17 @@ if (elapsed < 0) { continue; } - type = !isReply ? 'thread' : hasFile ? 'image' : 'reply'; + if (!isReply) { + type = 'thread'; + } else if (hasFile) { + if (!cooldown.hasFile) { + seconds = Math.max(seconds, 0); + continue; + } + type = 'image'; + } else { + type = 'reply'; + } maxTimer = Math.max(types[type] || 0, types[type + '_intra'] || 0); if (!((start <= now && now <= start + maxTimer * $.SECOND))) { QR.cooldown.unset(start); @@ -5393,7 +5403,7 @@ } if (seconds && Conf['Cooldown Prediction'] && hasFile && upSpd) { seconds -= Math.floor(post.file.size / upSpd * upSpdAccuracy); - seconds = Math.max(seconds, 0); + seconds = seconds > 0 ? seconds : 0; } update = seconds !== null || !!QR.cooldown.seconds; QR.cooldown.seconds = seconds; @@ -5615,6 +5625,9 @@ _this.name = 'name' in QR.persona.always ? QR.persona.always.name : prev ? prev.name : persona.name; _this.email = 'email' in QR.persona.always ? QR.persona.always.email : prev && !/^sage$/.test(prev.email) ? prev.email : persona.email; _this.sub = 'sub' in QR.persona.always ? QR.persona.always.sub : Conf['Remember Subject'] ? prev ? prev.sub : persona.sub : ''; + if (QR.nodes.flag) { + _this.flag = prev ? prev.flag : persona.flag; + } if (QR.selected === _this) { return _this.load(); } @@ -5646,7 +5659,7 @@ }; _Class.prototype.lock = function(lock) { - var name, _i, _len, _ref; + var name, node, _i, _len, _ref; if (lock == null) { lock = true; @@ -5655,10 +5668,12 @@ if (this !== QR.selected) { return; } - _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler']; + _ref = ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; - QR.nodes[name].disabled = lock; + if (node = QR.nodes[name]) { + node.disabled = lock; + } } this.nodes.rm.style.visibility = lock ? 'hidden' : ''; (lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput); @@ -5688,12 +5703,15 @@ }; _Class.prototype.load = function() { - var name, _i, _len, _ref; + var name, node, _i, _len, _ref; - _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename']; + _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; - QR.nodes[name].value = this[name] || null; + if (!(node = QR.nodes[name])) { + continue; + } + node.value = this[name] || node.dataset["default"] || null; } this.showFileData(); return QR.characterCount(); @@ -5707,7 +5725,7 @@ return; } name = input.dataset.name; - this[name] = input.value; + this[name] = input.value || input.dataset["default"] || null; switch (name) { case 'thread': return QR.status(); @@ -5731,15 +5749,18 @@ }; _Class.prototype.forceSave = function() { - var name, _i, _len, _ref; + var name, node, _i, _len, _ref; if (this !== QR.selected) { return; } - _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler']; + _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; - this.save(QR.nodes[name]); + if (!(node = QR.nodes[name])) { + continue; + } + this.save(node); } }; @@ -6077,7 +6098,7 @@ } }, dialog: function() { - var check, dialog, elm, i, items, key, mimeTypes, name, nodes, thread, value, _ref; + var check, dialog, elm, event, flagSelector, i, items, key, mimeTypes, name, node, nodes, save, thread, value, _ref; QR.nodes = nodes = { el: dialog = UI.dialog('qr', 'top:0;right:0;', "
") @@ -6133,8 +6154,15 @@ name: 'filetag', innerHTML: "\n\n\n\n\n\n" }); + nodes.flashTag.dataset["default"] = '4'; $.add(nodes.form, nodes.flashTag); } + if (flagSelector = $('.flagSelector')) { + nodes.flag = flagSelector.cloneNode(true); + nodes.flag.dataset.name = 'flag'; + nodes.flag.dataset["default"] = '0'; + $.add(nodes.form, nodes.flag); + } for (thread in g.BOARD.threads) { $.add(nodes.thread, $.el('option', { value: thread, @@ -6169,16 +6197,18 @@ return QR.selected.nodes.spoiler.click(); }); $.on(nodes.fileInput, 'change', QR.handleFiles); - items = ['name', 'email', 'sub', 'com', 'filename']; + items = ['name', 'email', 'sub', 'com', 'filename', 'flag']; i = 0; - while (name = items[i++]) { - $.on(nodes[name], 'input', function() { - return QR.selected.save(this); - }); - } - $.on(nodes.thread, 'change', function() { + save = function() { return QR.selected.save(this); - }); + }; + while (name = items[i++]) { + if (!(node = nodes[name])) { + continue; + } + event = node.nodeName === 'SELECT' ? 'change' : 'input'; + $.on(nodes[name], event, save); + } if (Conf['Remember QR Size']) { $.get('QR Size', '', function(item) { return nodes.com.style.cssText = item['QR Size']; @@ -6200,7 +6230,7 @@ }, preSubmitHooks: [], submit: function(e) { - var challenge, err, extra, filetag, hook, options, post, postData, response, textOnly, thread, threadID, _i, _len, _ref, _ref1; + var challenge, err, extra, filetag, formData, hook, options, post, response, textOnly, thread, threadID, _i, _len, _ref, _ref1; if (e != null) { e.preventDefault(); @@ -6264,7 +6294,7 @@ d.activeElement.blur(); } post.lock(); - postData = { + formData = { resto: threadID, name: post.name, email: post.email, @@ -6273,6 +6303,7 @@ upfile: post.file, filetag: filetag, spoiler: post.spoiler, + flag: post.flag, textonly: textOnly, mode: 'regist', pwd: QR.persona.pwd, @@ -6294,7 +6325,7 @@ } }; extra = { - form: $.formData(postData), + form: $.formData(formData), upCallbacks: { onload: function() { QR.req.isUploadFinished = true; @@ -6378,8 +6409,8 @@ threadID: threadID, postID: postID }); - postsCount = QR.posts.length; - QR.cooldown.auto = postsCount > 1 && isReply; + postsCount = QR.posts.length - 1; + QR.cooldown.auto = postsCount && isReply; if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) { notif = new Notification('Quick reply warning', { body: "You are running low on cached captchas. Cache count: " + captchasCount + ".", @@ -7494,7 +7525,7 @@ return; } DeleteLink.cooldown.counting = post; - length = 30; + length = 60; seconds = Math.ceil((length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND); return DeleteLink.cooldown.count(post, seconds, length, node); }, @@ -7808,7 +7839,6 @@ if (g.VIEW !== 'thread' || !Conf['Thread Updater']) { return; } - checked = Conf['Auto Update'] ? 'checked' : ''; if (Conf['Updater and Stats in Header']) { this.dialog = sc = $.el('span', { innerHTML: "", @@ -7828,6 +7858,7 @@ this.checkPostCount = 0; this.timer = $('#update-timer', sc); this.status = $('#update-status', sc); + this.isUpdating = Conf['Auto Update']; $.on(this.timer, 'click', ThreadUpdater.update); $.on(this.status, 'click', ThreadUpdater.update); subEntries = []; @@ -7845,7 +7876,7 @@ $.on(input, 'change', ThreadUpdater.cb.scrollBG); ThreadUpdater.cb.scrollBG(); } else if (input.name === 'Auto Update') { - $.on(input, 'change', ThreadUpdater.update); + $.on(input, 'change', ThreadUpdater.cb.update); } subEntries.push({ el: el @@ -7875,7 +7906,6 @@ ThreadUpdater.thread = this; ThreadUpdater.root = this.OP.nodes.root.parentNode; ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]; - ThreadUpdater.outdateCount = 0; ThreadUpdater.cb.interval.call($.el('input', { value: Conf['Interval'] })); @@ -7892,19 +7922,18 @@ beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA', cb: { online: function() { - if (ThreadUpdater.online = navigator.onLine) { + if (navigator.onLine) { ThreadUpdater.outdateCount = 0; ThreadUpdater.setInterval(); - ThreadUpdater.update(); ThreadUpdater.set('status', null, null); } else { ThreadUpdater.set('timer', null); ThreadUpdater.set('status', 'Offline', 'warning'); } - return ThreadUpdater.cb.autoUpdate(); + return ThreadUpdater.count(true); }, post: function(e) { - if (e.detail.threadID !== ThreadUpdater.thread.ID) { + if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) { return; } ThreadUpdater.outdateCount = 0; @@ -7934,9 +7963,7 @@ return; } ThreadUpdater.outdateCount = 0; - if (ThreadUpdater.seconds > ThreadUpdater.interval) { - return ThreadUpdater.setInterval(); - } + return ThreadUpdater.seconds = Math.min(ThreadUpdater.seconds, ThreadUpdater.interval); }, scrollBG: function() { return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() { @@ -7945,17 +7972,10 @@ return !d.hidden; }; }, - autoUpdate: function() { - if (ThreadUpdater.online) { - return ThreadUpdater.timeout(); - } else { - return clearTimeout(ThreadUpdater.timeoutID); - } - }, interval: function() { var val; - val = +this.value; + val = parseInt(this.value, 10); if (val < 1) { val = 1; } @@ -7966,9 +7986,9 @@ var klass, req, text, _ref; req = ThreadUpdater.req; + delete ThreadUpdater.req; if (e.type !== 'loadend') { req.onloadend = null; - delete ThreadUpdater.req; if (e.type === 'timeout') { ThreadUpdater.set('status', 'Retrying', null); ThreadUpdater.update(); @@ -7998,9 +8018,8 @@ ThreadUpdater.set('status', text, klass); } if (ThreadUpdater.postID) { - ThreadUpdater.cb.checkpost(); + return ThreadUpdater.cb.checkpost(); } - return delete ThreadUpdater.req; } }, setInterval: function() { @@ -8013,8 +8032,7 @@ } ThreadUpdater.seconds = Conf['Optional Increase'] ? (cur = [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j] > i) ? cur : i : i; ThreadUpdater.set('timer', ThreadUpdater.seconds++); - clearTimeout(ThreadUpdater.timeoutID); - return ThreadUpdater.timeout(); + return ThreadUpdater.count(true); }, intervalShortcut: function() { var settings; @@ -8036,20 +8054,29 @@ return el.className = klass; } }, + count: function(start) { + clearTimeout(ThreadUpdater.timeoutID); + if (start && ThreadUpdater.isUpdating && navigator.onLine) { + return ThreadUpdater.timeout(); + } + }, timeout: function() { + var sec; + ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); - ThreadUpdater.set('timer', --ThreadUpdater.seconds); - if (ThreadUpdater.seconds <= 0) { + sec = ThreadUpdater.seconds--; + ThreadUpdater.set('timer', sec); + if (sec <= 0) { return ThreadUpdater.update(); } }, update: function() { var url; - if (!ThreadUpdater.online) { + if (!navigator.onLine) { return; } - clearTimeout(ThreadUpdater.timeoutID); + ThreadUpdater.count(); if (Conf['Auto Update']) { ThreadUpdater.set('timer', '...'); } else { @@ -8235,27 +8262,6 @@ ThreadWatcher.fetchAllStatus(); this.db.save(); } - $.get('WatchedThreads', null, function(_arg) { - var WatchedThreads, boardID, data, threadID, threads, _ref; - - WatchedThreads = _arg.WatchedThreads; - if (!WatchedThreads) { - return; - } - _ref = ThreadWatcher.convert(WatchedThreads); - for (boardID in _ref) { - threads = _ref[boardID]; - for (threadID in threads) { - data = threads[threadID]; - ThreadWatcher.db.set({ - boardID: boardID, - threadID: threadID, - val: data - }); - } - } - return $["delete"]('WatchedThreads'); - }); return Thread.prototype.callbacks.push({ name: 'Thread Watcher', cb: this.node diff --git a/builds/crx/script.js b/builds/crx/script.js index 910746ac2..141e5a56c 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -5266,7 +5266,8 @@ persona = { name: post.name, email: /^sage$/.test(post.email) ? persona.email : post.email, - sub: Conf['Remember Subject'] ? post.sub : void 0 + sub: Conf['Remember Subject'] ? post.sub : void 0, + flag: post.flag }; return $.set('QR.persona', persona); }); @@ -5330,12 +5331,11 @@ delay: delay }; } else { - if (post.file) { + if (hasFile = !!post.file) { upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND); QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2; QR.cooldown.upSpd = upSpd; } - hasFile = !!post.file; cooldown = { isReply: isReply, hasFile: hasFile, @@ -5392,7 +5392,17 @@ if (elapsed < 0) { continue; } - type = !isReply ? 'thread' : hasFile ? 'image' : 'reply'; + if (!isReply) { + type = 'thread'; + } else if (hasFile) { + if (!cooldown.hasFile) { + seconds = Math.max(seconds, 0); + continue; + } + type = 'image'; + } else { + type = 'reply'; + } maxTimer = Math.max(types[type] || 0, types[type + '_intra'] || 0); if (!((start <= now && now <= start + maxTimer * $.SECOND))) { QR.cooldown.unset(start); @@ -5405,7 +5415,7 @@ } if (seconds && Conf['Cooldown Prediction'] && hasFile && upSpd) { seconds -= Math.floor(post.file.size / upSpd * upSpdAccuracy); - seconds = Math.max(seconds, 0); + seconds = seconds > 0 ? seconds : 0; } update = seconds !== null || !!QR.cooldown.seconds; QR.cooldown.seconds = seconds; @@ -5621,6 +5631,9 @@ _this.name = 'name' in QR.persona.always ? QR.persona.always.name : prev ? prev.name : persona.name; _this.email = 'email' in QR.persona.always ? QR.persona.always.email : prev && !/^sage$/.test(prev.email) ? prev.email : persona.email; _this.sub = 'sub' in QR.persona.always ? QR.persona.always.sub : Conf['Remember Subject'] ? prev ? prev.sub : persona.sub : ''; + if (QR.nodes.flag) { + _this.flag = prev ? prev.flag : persona.flag; + } if (QR.selected === _this) { return _this.load(); } @@ -5652,7 +5665,7 @@ }; _Class.prototype.lock = function(lock) { - var name, _i, _len, _ref; + var name, node, _i, _len, _ref; if (lock == null) { lock = true; @@ -5661,10 +5674,12 @@ if (this !== QR.selected) { return; } - _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler']; + _ref = ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; - QR.nodes[name].disabled = lock; + if (node = QR.nodes[name]) { + node.disabled = lock; + } } this.nodes.rm.style.visibility = lock ? 'hidden' : ''; (lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput); @@ -5694,12 +5709,15 @@ }; _Class.prototype.load = function() { - var name, _i, _len, _ref; + var name, node, _i, _len, _ref; - _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename']; + _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; - QR.nodes[name].value = this[name] || null; + if (!(node = QR.nodes[name])) { + continue; + } + node.value = this[name] || node.dataset["default"] || null; } this.showFileData(); return QR.characterCount(); @@ -5713,7 +5731,7 @@ return; } name = input.dataset.name; - this[name] = input.value; + this[name] = input.value || input.dataset["default"] || null; switch (name) { case 'thread': return QR.status(); @@ -5737,15 +5755,18 @@ }; _Class.prototype.forceSave = function() { - var name, _i, _len, _ref; + var name, node, _i, _len, _ref; if (this !== QR.selected) { return; } - _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler']; + _ref = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { name = _ref[_i]; - this.save(QR.nodes[name]); + if (!(node = QR.nodes[name])) { + continue; + } + this.save(node); } }; @@ -6081,7 +6102,7 @@ } }, dialog: function() { - var check, dialog, i, items, key, mimeTypes, name, nodes, thread, value, _ref; + var check, dialog, event, flagSelector, i, items, key, mimeTypes, name, node, nodes, save, thread, value, _ref; QR.nodes = nodes = { el: dialog = UI.dialog('qr', 'top:0;right:0;', " ") @@ -6137,8 +6158,15 @@ name: 'filetag', innerHTML: "\n\n\n\n\n\n" }); + nodes.flashTag.dataset["default"] = '4'; $.add(nodes.form, nodes.flashTag); } + if (flagSelector = $('.flagSelector')) { + nodes.flag = flagSelector.cloneNode(true); + nodes.flag.dataset.name = 'flag'; + nodes.flag.dataset["default"] = '0'; + $.add(nodes.form, nodes.flag); + } for (thread in g.BOARD.threads) { $.add(nodes.thread, $.el('option', { value: thread, @@ -6167,16 +6195,18 @@ return QR.selected.nodes.spoiler.click(); }); $.on(nodes.fileInput, 'change', QR.handleFiles); - items = ['name', 'email', 'sub', 'com', 'filename']; + items = ['name', 'email', 'sub', 'com', 'filename', 'flag']; i = 0; - while (name = items[i++]) { - $.on(nodes[name], 'input', function() { - return QR.selected.save(this); - }); - } - $.on(nodes.thread, 'change', function() { + save = function() { return QR.selected.save(this); - }); + }; + while (name = items[i++]) { + if (!(node = nodes[name])) { + continue; + } + event = node.nodeName === 'SELECT' ? 'change' : 'input'; + $.on(nodes[name], event, save); + } QR.persona.init(); new QR.post(true); QR.status(); @@ -6187,7 +6217,7 @@ }, preSubmitHooks: [], submit: function(e) { - var challenge, err, extra, filetag, hook, options, post, postData, response, textOnly, thread, threadID, _i, _len, _ref, _ref1; + var challenge, err, extra, filetag, formData, hook, options, post, response, textOnly, thread, threadID, _i, _len, _ref, _ref1; if (e != null) { e.preventDefault(); @@ -6251,7 +6281,7 @@ d.activeElement.blur(); } post.lock(); - postData = { + formData = { resto: threadID, name: post.name, email: post.email, @@ -6260,6 +6290,7 @@ upfile: post.file, filetag: filetag, spoiler: post.spoiler, + flag: post.flag, textonly: textOnly, mode: 'regist', pwd: QR.persona.pwd, @@ -6281,7 +6312,7 @@ } }; extra = { - form: $.formData(postData), + form: $.formData(formData), upCallbacks: { onload: function() { QR.req.isUploadFinished = true; @@ -6365,8 +6396,8 @@ threadID: threadID, postID: postID }); - postsCount = QR.posts.length; - QR.cooldown.auto = postsCount > 1 && isReply; + postsCount = QR.posts.length - 1; + QR.cooldown.auto = postsCount && isReply; if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) { notif = new Notification('Quick reply warning', { body: "You are running low on cached captchas. Cache count: " + captchasCount + ".", @@ -7481,7 +7512,7 @@ return; } DeleteLink.cooldown.counting = post; - length = 30; + length = 60; seconds = Math.ceil((length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND); return DeleteLink.cooldown.count(post, seconds, length, node); }, @@ -7795,7 +7826,6 @@ if (g.VIEW !== 'thread' || !Conf['Thread Updater']) { return; } - checked = Conf['Auto Update'] ? 'checked' : ''; if (Conf['Updater and Stats in Header']) { this.dialog = sc = $.el('span', { innerHTML: "", @@ -7815,6 +7845,7 @@ this.checkPostCount = 0; this.timer = $('#update-timer', sc); this.status = $('#update-status', sc); + this.isUpdating = Conf['Auto Update']; $.on(this.timer, 'click', ThreadUpdater.update); $.on(this.status, 'click', ThreadUpdater.update); subEntries = []; @@ -7832,7 +7863,7 @@ $.on(input, 'change', ThreadUpdater.cb.scrollBG); ThreadUpdater.cb.scrollBG(); } else if (input.name === 'Auto Update') { - $.on(input, 'change', ThreadUpdater.update); + $.on(input, 'change', ThreadUpdater.cb.update); } subEntries.push({ el: el @@ -7862,7 +7893,6 @@ ThreadUpdater.thread = this; ThreadUpdater.root = this.OP.nodes.root.parentNode; ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]; - ThreadUpdater.outdateCount = 0; ThreadUpdater.cb.interval.call($.el('input', { value: Conf['Interval'] })); @@ -7879,19 +7909,18 @@ beep: 'data:audio/wav;base64,UklGRjQDAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAc21wbDwAAABBAAADAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkYXRhzAIAAGMms8em0tleMV4zIpLVo8nhfSlcPR102Ki+5JspVEkdVtKzs+K1NEhUIT7DwKrcy0g6WygsrM2k1NpiLl0zIY/WpMrjgCdbPhxw2Kq+5Z4qUkkdU9K1s+K5NkVTITzBwqnczko3WikrqM+l1NxlLF0zIIvXpsnjgydZPhxs2ay95aIrUEkdUdC3suK8N0NUIjq+xKrcz002WioppdGm091pK1w0IIjYp8jkhydXPxxq2K295aUrTkoeTs65suK+OUFUIzi7xqrb0VA0WSoootKm0t5tKlo1H4TYqMfkiydWQBxm16+85actTEseS8y7seHAPD9TIza5yKra01QyWSson9On0d5wKVk2H4DYqcfkjidUQB1j1rG75KsvSkseScu8seDCPz1TJDW2yara1FYxWSwnm9Sn0N9zKVg2H33ZqsXkkihSQR1g1bK65K0wSEsfR8i+seDEQTxUJTOzy6rY1VowWC0mmNWoz993KVc3H3rYq8TklSlRQh1d1LS647AyR0wgRMbAsN/GRDpTJTKwzKrX1l4vVy4lldWpzt97KVY4IXbUr8LZljVPRCxhw7W3z6ZISkw1VK+4sMWvXEhSPk6buay9sm5JVkZNiLWqtrJ+TldNTnquqbCwilZXU1BwpKirrpNgWFhTaZmnpquZbFlbVmWOpaOonHZcXlljhaGhpZ1+YWBdYn2cn6GdhmdhYGN3lp2enIttY2Jjco+bnJuOdGZlZXCImJqakHpoZ2Zug5WYmZJ/bGlobX6RlpeSg3BqaW16jZSVkoZ0bGtteImSk5KIeG5tbnaFkJKRinxxbm91gY2QkIt/c3BwdH6Kj4+LgnZxcXR8iI2OjIR5c3J0e4WLjYuFe3VzdHmCioyLhn52dHR5gIiKioeAeHV1eH+GiYqHgXp2dnh9hIiJh4J8eHd4fIKHiIeDfXl4eHyBhoeHhH96eHmA', cb: { online: function() { - if (ThreadUpdater.online = navigator.onLine) { + if (navigator.onLine) { ThreadUpdater.outdateCount = 0; ThreadUpdater.setInterval(); - ThreadUpdater.update(); ThreadUpdater.set('status', null, null); } else { ThreadUpdater.set('timer', null); ThreadUpdater.set('status', 'Offline', 'warning'); } - return ThreadUpdater.cb.autoUpdate(); + return ThreadUpdater.count(true); }, post: function(e) { - if (e.detail.threadID !== ThreadUpdater.thread.ID) { + if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) { return; } ThreadUpdater.outdateCount = 0; @@ -7921,9 +7950,7 @@ return; } ThreadUpdater.outdateCount = 0; - if (ThreadUpdater.seconds > ThreadUpdater.interval) { - return ThreadUpdater.setInterval(); - } + return ThreadUpdater.seconds = Math.min(ThreadUpdater.seconds, ThreadUpdater.interval); }, scrollBG: function() { return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() { @@ -7932,17 +7959,10 @@ return !d.hidden; }; }, - autoUpdate: function() { - if (ThreadUpdater.online) { - return ThreadUpdater.timeout(); - } else { - return clearTimeout(ThreadUpdater.timeoutID); - } - }, interval: function() { var val; - val = +this.value; + val = parseInt(this.value, 10); if (val < 1) { val = 1; } @@ -7953,9 +7973,9 @@ var klass, req, text, _ref; req = ThreadUpdater.req; + delete ThreadUpdater.req; if (e.type !== 'loadend') { req.onloadend = null; - delete ThreadUpdater.req; if (e.type === 'timeout') { ThreadUpdater.set('status', 'Retrying', null); ThreadUpdater.update(); @@ -7985,9 +8005,8 @@ ThreadUpdater.set('status', text, klass); } if (ThreadUpdater.postID) { - ThreadUpdater.cb.checkpost(); + return ThreadUpdater.cb.checkpost(); } - return delete ThreadUpdater.req; } }, setInterval: function() { @@ -8000,8 +8019,7 @@ } ThreadUpdater.seconds = Conf['Optional Increase'] ? (cur = [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j] > i) ? cur : i : i; ThreadUpdater.set('timer', ThreadUpdater.seconds++); - clearTimeout(ThreadUpdater.timeoutID); - return ThreadUpdater.timeout(); + return ThreadUpdater.count(true); }, intervalShortcut: function() { var settings; @@ -8023,20 +8041,29 @@ return el.className = klass; } }, + count: function(start) { + clearTimeout(ThreadUpdater.timeoutID); + if (start && ThreadUpdater.isUpdating && navigator.onLine) { + return ThreadUpdater.timeout(); + } + }, timeout: function() { + var sec; + ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); - ThreadUpdater.set('timer', --ThreadUpdater.seconds); - if (ThreadUpdater.seconds <= 0) { + sec = ThreadUpdater.seconds--; + ThreadUpdater.set('timer', sec); + if (sec <= 0) { return ThreadUpdater.update(); } }, update: function() { var url; - if (!ThreadUpdater.online) { + if (!navigator.onLine) { return; } - clearTimeout(ThreadUpdater.timeoutID); + ThreadUpdater.count(); if (Conf['Auto Update']) { ThreadUpdater.set('timer', '...'); } else { @@ -8222,27 +8249,6 @@ ThreadWatcher.fetchAllStatus(); this.db.save(); } - $.get('WatchedThreads', null, function(_arg) { - var WatchedThreads, boardID, data, threadID, threads, _ref; - - WatchedThreads = _arg.WatchedThreads; - if (!WatchedThreads) { - return; - } - _ref = ThreadWatcher.convert(WatchedThreads); - for (boardID in _ref) { - threads = _ref[boardID]; - for (threadID in threads) { - data = threads[threadID]; - ThreadWatcher.db.set({ - boardID: boardID, - threadID: threadID, - val: data - }); - } - } - return $["delete"]('WatchedThreads'); - }); return Thread.prototype.callbacks.push({ name: 'Thread Watcher', cb: this.node diff --git a/css/style.css b/css/style.css index c2646837c..dbba61fa3 100644 --- a/css/style.css +++ b/css/style.css @@ -581,7 +581,7 @@ a.hide-announcement { align-self: stretch; flex: 1; } -#qr select { +#qr select[data-name=thread] { margin: 0; -webkit-appearance: none; -moz-appearance: none; @@ -794,7 +794,6 @@ a.hide-announcement { } #file-n-submit-container input { margin: 0; - padding: 0; } #file-n-submit input[type='submit'] { order: 1; diff --git a/json/archives.json b/json/archives.json new file mode 100644 index 000000000..e2654302b --- /dev/null +++ b/json/archives.json @@ -0,0 +1,110 @@ +[{ + "uid": 0, + "name": "Foolz", + "domain": "archive.foolz.us", + "http": true, + "https": true, + "software": "foolfuuka", + "boards": ["a", "co", "gd", "jp", "m", "q", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"], + "files": ["a", "gd", "jp", "m", "q", "tg", "vg", "vp", "vr", "wsg"] +}, { + "uid": 1, + "name": "NSFW Foolz", + "domain": "nsfw.foolz.us", + "http": true, + "https": true, + "software": "foolfuuka", + "boards": ["u"], + "files": ["u"] +}, { + "uid": 2, + "name": "The Dark Cave", + "domain": "archive.thedarkcave.org", + "http": true, + "https": true, + "software": "foolfuuka", + "boards": ["c", "int", "out", "po"], + "files": ["c", "po"] +}, { + "uid": 3, + "name": "4plebs", + "domain": "archive.4plebs.org", + "http": true, + "https": true, + "software": "foolfuuka", + "boards": ["hr", "tg", "tv", "x"], + "files": ["hr", "tg", "tv", "x"] +}, { + "uid": 4, + "name": "Nyafuu", + "domain": "archive.nyafuu.org", + "http": true, + "https": true, + "software": "foolfuuka", + "boards": ["c", "w", "wg"], + "files": ["c", "w", "wg"] +}, { + "uid": 11, + "name": "Foolz a Shit", + "domain": "archive.foolzashit.com", + "http": true, + "https": true, + "software": "foolfuuka", + "boards": ["adv", "asp", "cm", "d", "e", "i", "lgbt", "n", "o", "p", "pol", "s", "s4s", "t", "trv", "y"], + "files": ["cm", "d", "e", "i", "n", "o", "p", "s", "trv", "y"] +}, { + "uid": 12, + "name": "FapArchive", + "domain": "fuuka.worldathleticproject.org", + "http": true, + "https": true, + "software": "foolfuuka", + "boards": ["b", "e", "h", "hc", "p", "s", "soc", "sp", "u"], + "files": ["b", "e", "h", "hc", "p", "s", "soc", "sp", "u"] +}, { + "uid": 7, + "name": "Install Gentoo", + "domain": "archive.installgentoo.net", + "http": false, + "https": true, + "software": "fuuka", + "boards": ["diy", "g", "sci"], + "files": [] +}, { + "uid": 8, + "name": "Rebecca Black Tech", + "domain": "rbt.asia", + "http": true, + "https": true, + "software": "fuuka", + "boards": ["cgl", "g", "mu", "w"], + "files": ["cgl", "g", "mu", "w"] +}, { + "uid": 9, + "name": "Heinessen", + "domain": "archive.heinessen.com", + "http": true, + "https": false, + "software": "fuuka", + "boards": ["an", "fit", "k", "mlp", "r9k", "toy"], + "files": ["an", "fit", "k", "r9k", "toy"] +}, { + "uid": 10, + "name": "warosu", + "domain": "fuuka.warosu.org", + "http": true, + "https": true, + "software": "fuuka", + "boards": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"], + "files": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"] +}, { + "uid": 13, + "name": "Foolz Beta", + "domain": "beta.foolz.us", + "http": true, + "https": true, + "withCredentials": true, + "software": "foolfuuka", + "boards": ["a", "co", "d", "gd", "h", "jp", "m", "mlp", "q", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"], + "files": ["a", "d", "gd", "h", "jp", "m", "q", "tg", "u", "vg", "vp", "vr", "wsg"] +}] diff --git a/package.json b/package.json index 81fe6b8a5..5beac892b 100755 --- a/package.json +++ b/package.json @@ -23,14 +23,14 @@ "font-awesome": "git://github.com/MayhemYDG/Font-Awesome.git#df4285951124f9ca1f3907438462e5ba9e464bcb", "grunt": "~0.4.1", "grunt-bump": "~0.0.11", - "grunt-concurrent": "~0.3.0", + "grunt-concurrent": "~0.3.1", "grunt-contrib-clean": "~0.5.0", "grunt-contrib-coffee": "~0.7.0", "grunt-contrib-compress": "~0.5.2", "grunt-contrib-concat": "~0.3.0", "grunt-contrib-copy": "~0.4.1", - "grunt-contrib-watch": "~0.5.0", - "grunt-shell": "~0.3.1", + "grunt-contrib-watch": "~0.5.3", + "grunt-shell": "~0.4.0", "load-grunt-tasks": "~0.1.0" }, "repository": { diff --git a/src/Filtering/Anonymize.coffee b/src/Filtering/Anonymize.coffee index 1db6de4ad..d5b80f99c 100755 --- a/src/Filtering/Anonymize.coffee +++ b/src/Filtering/Anonymize.coffee @@ -15,4 +15,4 @@ Anonymize = delete @nodes.tripcode if @info.email $.replace email, name - delete @nodes.email \ No newline at end of file + delete @nodes.email diff --git a/src/General/Config.coffee b/src/General/Config.coffee index b851d9a19..0b678177c 100755 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -453,9 +453,8 @@ Config = #/Mod$/;highlight:mod;op:yes # Set a custom class for moot: #/Admin$/;highlight:moot;op:yes -""" - - email: "" + """ + email: "" subject: """ # Filter Generals on /v/: #/general/i;boards:v;op:only diff --git a/src/Menu/DeleteLink.coffee b/src/Menu/DeleteLink.coffee index dca9e889f..1805047f8 100755 --- a/src/Menu/DeleteLink.coffee +++ b/src/Menu/DeleteLink.coffee @@ -84,7 +84,7 @@ DeleteLink = delete DeleteLink.cooldown.counting return DeleteLink.cooldown.counting = post - length = 30 + length = 60 seconds = Math.ceil (length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND DeleteLink.cooldown.count post, seconds, length, node count: (post, seconds, length, node) -> diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index 32c5c668b..85850db95 100755 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -2,8 +2,6 @@ ThreadUpdater = init: -> return if g.VIEW isnt 'thread' or !Conf['Thread Updater'] - checked = if Conf['Auto Update'] then 'checked' else '' - if Conf['Updater and Stats in Header'] @dialog = sc = $.el 'span', innerHTML: "" @@ -16,12 +14,13 @@ ThreadUpdater = $.addClass doc, 'float' $.ready => $.addClass doc, 'float' - $.add d.body, sc + $.add d.body, sc @checkPostCount = 0 @timer = $ '#update-timer', sc @status = $ '#update-status', sc + @isUpdating = Conf['Auto Update'] $.on @timer, 'click', ThreadUpdater.update $.on @status, 'click', ThreadUpdater.update @@ -38,7 +37,7 @@ ThreadUpdater = $.on input, 'change', ThreadUpdater.cb.scrollBG ThreadUpdater.cb.scrollBG() else if input.name is 'Auto Update' - $.on input, 'change', ThreadUpdater.update + $.on input, 'change', ThreadUpdater.cb.update subEntries.push el: el settings = $.el 'span', @@ -63,7 +62,6 @@ ThreadUpdater = ThreadUpdater.thread = @ ThreadUpdater.root = @OP.nodes.root.parentNode ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0] - ThreadUpdater.outdateCount = 0 ThreadUpdater.cb.interval.call $.el 'input', value: Conf['Interval'] @@ -81,17 +79,16 @@ ThreadUpdater = cb: online: -> - if ThreadUpdater.online = navigator.onLine + if navigator.onLine ThreadUpdater.outdateCount = 0 ThreadUpdater.setInterval() - ThreadUpdater.update() ThreadUpdater.set 'status', null, null else ThreadUpdater.set 'timer', null ThreadUpdater.set 'status', 'Offline', 'warning' - ThreadUpdater.cb.autoUpdate() + ThreadUpdater.count true post: (e) -> - return unless e.detail.threadID is ThreadUpdater.thread.ID + return unless ThreadUpdater.isUpdating and e.detail.threadID is ThreadUpdater.thread.ID ThreadUpdater.outdateCount = 0 setTimeout ThreadUpdater.update, 1000 if ThreadUpdater.seconds > 2 checkpost: (e) -> @@ -110,28 +107,22 @@ ThreadUpdater = return if d.hidden # Reset the counter when we focus this tab. ThreadUpdater.outdateCount = 0 - if ThreadUpdater.seconds > ThreadUpdater.interval - ThreadUpdater.setInterval() + ThreadUpdater.seconds = Math.min ThreadUpdater.seconds, ThreadUpdater.interval scrollBG: -> ThreadUpdater.scrollBG = if Conf['Scroll BG'] -> true else -> not d.hidden - autoUpdate: -> - if ThreadUpdater.online - ThreadUpdater.timeout() - else - clearTimeout ThreadUpdater.timeoutID interval: -> - val = +@value + val = parseInt @value, 10 if val < 1 then val = 1 ThreadUpdater.interval = @value = val $.cb.value.call @ load: (e) -> {req} = ThreadUpdater + delete ThreadUpdater.req if e.type isnt 'loadend' # timeout or abort req.onloadend = null - delete ThreadUpdater.req if e.type is 'timeout' ThreadUpdater.set 'status', 'Retrying', null ThreadUpdater.update() @@ -161,8 +152,6 @@ ThreadUpdater = if ThreadUpdater.postID ThreadUpdater.cb.checkpost() - delete ThreadUpdater.req - setInterval: -> i = ThreadUpdater.interval # Math.min/max is provably slow: http://jsperf.com/math-s-min-max-vs-homemade/5 @@ -176,8 +165,7 @@ ThreadUpdater = else i ThreadUpdater.set 'timer', ThreadUpdater.seconds++ - clearTimeout ThreadUpdater.timeoutID - ThreadUpdater.timeout() + ThreadUpdater.count true intervalShortcut: -> Settings.open 'Advanced' @@ -194,14 +182,19 @@ ThreadUpdater = el.textContent = text el.className = klass if klass isnt undefined + count: (start) -> + clearTimeout ThreadUpdater.timeoutID + ThreadUpdater.timeout() if start and ThreadUpdater.isUpdating and navigator.onLine + timeout: -> ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000 - ThreadUpdater.set 'timer', --ThreadUpdater.seconds - ThreadUpdater.update() if ThreadUpdater.seconds <= 0 + sec = ThreadUpdater.seconds-- + ThreadUpdater.set 'timer', sec + ThreadUpdater.update() if sec <= 0 update: -> - return unless ThreadUpdater.online - clearTimeout ThreadUpdater.timeoutID + return unless navigator.onLine + ThreadUpdater.count() if Conf['Auto Update'] ThreadUpdater.set 'timer', '...' else diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee index 073966965..7f1712559 100755 --- a/src/Monitoring/ThreadWatcher.coffee +++ b/src/Monitoring/ThreadWatcher.coffee @@ -31,14 +31,6 @@ ThreadWatcher = ThreadWatcher.fetchAllStatus() @db.save() - # XXX tmp conversion from old to new format - $.get 'WatchedThreads', null, ({WatchedThreads}) -> - return unless WatchedThreads - for boardID, threads of ThreadWatcher.convert WatchedThreads - for threadID, data of threads - ThreadWatcher.db.set {boardID, threadID, val: data} - $.delete 'WatchedThreads' - Thread::callbacks.push name: 'Thread Watcher' cb: @node diff --git a/src/Posting/QuickReply.coffee b/src/Posting/QuickReply.coffee index 77ae67d70..a585c6b48 100755 --- a/src/Posting/QuickReply.coffee +++ b/src/Posting/QuickReply.coffee @@ -268,6 +268,7 @@ QR = 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 cooldown: @@ -307,11 +308,10 @@ QR = if delay cooldown = {delay} else - if post.file + if hasFile = !!post.file upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND) QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2 QR.cooldown.upSpd = upSpd - hasFile = !!post.file cooldown = {isReply, hasFile, threadID} QR.cooldown.cooldowns[start] = cooldown $.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns @@ -358,24 +358,28 @@ QR = 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 + # reply cooldown with a reply, thread cooldown with a thread elapsed = Math.floor (now - start) / $.SECOND continue if elapsed < 0 # clock changed since then? - type = unless isReply - 'thread' + unless isReply + type = 'thread' else if hasFile - 'image' + # You can post an image reply immediately after a non-image reply. + unless cooldown.hasFile + seconds = Math.max seconds, 0 + continue + type = 'image' else - 'reply' - maxTimer = Math.max types[type] or 0, types[type + '_intra'] or 0 + type = '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 + type += '_intra' if isReply and +post.thread is cooldown.threadID seconds = Math.max seconds, types[type] - elapsed if seconds and Conf['Cooldown Prediction'] and hasFile and upSpd seconds -= Math.floor post.file.size / upSpd * upSpdAccuracy - seconds = Math.max seconds, 0 + seconds = if seconds > 0 then seconds else 0 # Update the status when we change posting type. # Don't get stuck at some random number. # Don't interfere with progress status updates. @@ -565,6 +569,12 @@ QR = if prev then prev.sub else persona.sub else '' + + if QR.nodes.flag + @flag = if prev + prev.flag + else + persona.flag @load() if QR.selected is @ # load persona @select() if select @unlock() @@ -586,8 +596,8 @@ QR = lock: (lock=true) -> @isLocked = lock return unless @ is QR.selected - for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler'] - QR.nodes[name].disabled = lock + for name in ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag'] when node = QR.nodes[name] + node.disabled = lock @nodes.rm.style.visibility = if lock then 'hidden' else '' (if lock then $.off else $.on) QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput @nodes.spoiler.disabled = lock @@ -612,8 +622,9 @@ QR = load: -> # Load this post's values. - for name in ['thread', 'name', 'email', 'sub', 'com', 'filename'] - QR.nodes[name].value = @[name] or null + for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'] + continue unless node = QR.nodes[name] + node.value = @[name] or node.dataset.default or null @showFileData() QR.characterCount() @@ -622,7 +633,7 @@ QR = @spoiler = input.checked return {name} = input.dataset - @[name] = input.value + @[name] = input.value or input.dataset.default or null switch name when 'thread' QR.status() @@ -647,8 +658,9 @@ QR = return unless @ is QR.selected # Do this in case people use extensions # that do not trigger the `input` event. - for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler'] - @save QR.nodes[name] + for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag'] + continue unless node = QR.nodes[name] + @save node return setFile: (@file) -> @@ -948,7 +960,13 @@ QR = """ + nodes.flashTag.dataset.default = '4' $.add nodes.form, nodes.flashTag + if flagSelector = $ '.flagSelector' + nodes.flag = flagSelector.cloneNode true + nodes.flag.dataset.name = 'flag' + nodes.flag.dataset.default = '0' + $.add nodes.form, nodes.flag # Make a list of threads. for thread of g.BOARD.threads @@ -978,12 +996,15 @@ QR = $.on nodes.fileExtras, 'click', (e) -> e.stopPropagation() $.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click() $.on nodes.fileInput, 'change', QR.handleFiles + # save selected post's data - items = ['name', 'email', 'sub', 'com', 'filename'] + items = ['name', 'email', 'sub', 'com', 'filename', 'flag'] i = 0 + save = -> QR.selected.save @ while name = items[i++] - $.on nodes[name], 'input', -> QR.selected.save @ - $.on nodes.thread, 'change', -> QR.selected.save @ + continue unless node = nodes[name] + event = if node.nodeName is 'SELECT' then 'change' else 'input' + $.on nodes[name], event, save <% if (type === 'userscript') { %> if Conf['Remember QR Size'] @@ -1029,7 +1050,7 @@ QR = # prevent errors if threadID is 'new' threadID = null - if g.BOARD.ID is 'vg' and !post.sub + if g.BOARD.ID is 'vg' and !post.sub err = 'New threads require a subject.' else unless post.file or textOnly = !!$ 'input[name=textonly]', $.id 'postForm' err = 'No file selected.' @@ -1065,7 +1086,7 @@ QR = post.lock() - postData = + formData = resto: threadID name: post.name email: post.email @@ -1074,6 +1095,7 @@ QR = upfile: post.file filetag: filetag spoiler: post.spoiler + flag: post.flag textonly: textOnly mode: 'regist' pwd: QR.persona.pwd @@ -1097,7 +1119,7 @@ QR = [Banned?] [More info] """ extra = - form: $.formData postData + form: $.formData formData upCallbacks: onload: -> # Upload done, waiting for server response. @@ -1202,9 +1224,9 @@ QR = postID } - # Enable auto-posting if we have stuff to post, disable it otherwise. - postsCount = QR.posts.length - QR.cooldown.auto = postsCount > 1 and isReply + # Enable auto-posting if we have stuff left to post, disable it otherwise. + postsCount = QR.posts.length - 1 + QR.cooldown.auto = postsCount and isReply if QR.cooldown.auto and QR.captcha.isEnabled and (captchasCount = QR.captcha.captchas.length) < 3 and captchasCount < postsCount notif = new Notification 'Quick reply warning', body: "You are running low on cached captchas. Cache count: #{captchasCount}."