Merge branch 'v3' of git://github.com/MayhemYDG/4chan-x into v3

Conflicts:
	CHANGELOG.md
	Gruntfile.coffee
	json/archives.json
	package.json
	src/Filtering/Anonymize.coffee
	src/General/Config.coffee
	src/General/lib/post.class
	src/Linkification/Linkify.coffee
	src/Monitoring/ThreadUpdater.coffee
	src/Posting/QuickReply.coffee
This commit is contained in:
Zixaphir 2013-09-20 05:22:42 -07:00
commit 847078db53
13 changed files with 374 additions and 246 deletions

View File

@ -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 ### v1.2.39
*2013-09-19* *2013-09-19*
**seaweedchan**: **seaweedchan**:
- Fix thread updater bug introduced in last version - Fix thread updater bug introduced in last version

View File

@ -1,21 +1,14 @@
module.exports = (grunt) -> module.exports = (grunt) ->
concatOptions =
process: Object.create(null, data:
get: -> grunt.config 'pkg'
enumerable: true
)
shellOptions =
stdout: true
stderr: true
failOnError: true
# Project configuration. # Project configuration.
grunt.initConfig grunt.initConfig
pkg: grunt.file.readJSON 'package.json' pkg: grunt.file.readJSON 'package.json'
concat: concat:
options: process: Object.create(null, data:
get: -> grunt.config 'pkg'
enumerable: true
)
coffee: coffee:
options: concatOptions
src: [ src: [
'src/General/Config.coffee' 'src/General/Config.coffee'
'src/General/Globals.coffee' 'src/General/Globals.coffee'
@ -41,13 +34,11 @@ module.exports = (grunt) ->
dest: 'tmp-<%= pkg.type %>/script.coffee' dest: 'tmp-<%= pkg.type %>/script.coffee'
meta: meta:
options: concatOptions
files: files:
'LICENSE': 'src/General/meta/banner.js', 'LICENSE': 'src/General/meta/banner.js',
'latest.js': 'src/General/meta/latest.js' 'latest.js': 'src/General/meta/latest.js'
crx: crx:
options: concatOptions
files: files:
'builds/crx/manifest.json': 'src/General/meta/manifest.json' 'builds/crx/manifest.json': 'src/General/meta/manifest.json'
'builds/crx/script.js': [ 'builds/crx/script.js': [
@ -57,7 +48,6 @@ module.exports = (grunt) ->
'tmp-<%= pkg.type %>/script.js' 'tmp-<%= pkg.type %>/script.js'
] ]
userscript: userscript:
options: concatOptions
files: files:
'builds/<%= pkg.name %>.meta.js': 'src/General/meta/metadata.js' 'builds/<%= pkg.name %>.meta.js': 'src/General/meta/metadata.js'
'builds/<%= pkg.name %>.user.js': [ 'builds/<%= pkg.name %>.user.js': [
@ -96,22 +86,23 @@ module.exports = (grunt) ->
push: false push: false
shell: shell:
options:
stdout: true
stderr: true
failOnError: true
commit: commit:
options: shellOptions
command: [ command: [
'git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>."' 'git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>."'
'git tag -a <%= pkg.version %> -m "<%= 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 %>."' 'git tag -af stable -m "<%= pkg.meta.name %> v<%= pkg.version %>."'
].join ' && ' ].join ' && '
push: push:
options: shellOptions
command: 'git push origin --tags -f && git push origin --all' command: 'git push origin --tags -f && git push origin --all'
watch: watch:
options:
interrupt: true
all: all:
options:
interrupt: true
files: [ files: [
'Gruntfile.coffee' 'Gruntfile.coffee'
'package.json' 'package.json'

View File

@ -5254,7 +5254,8 @@
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 sub: Conf['Remember Subject'] ? post.sub : void 0,
flag: post.flag
}; };
return $.set('QR.persona', persona); return $.set('QR.persona', persona);
}); });
@ -5318,12 +5319,11 @@
delay: delay delay: delay
}; };
} else { } else {
if (post.file) { if (hasFile = !!post.file) {
upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND); upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND);
QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2; QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2;
QR.cooldown.upSpd = upSpd; QR.cooldown.upSpd = upSpd;
} }
hasFile = !!post.file;
cooldown = { cooldown = {
isReply: isReply, isReply: isReply,
hasFile: hasFile, hasFile: hasFile,
@ -5380,7 +5380,17 @@
if (elapsed < 0) { if (elapsed < 0) {
continue; 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); maxTimer = Math.max(types[type] || 0, types[type + '_intra'] || 0);
if (!((start <= now && now <= start + maxTimer * $.SECOND))) { if (!((start <= now && now <= start + maxTimer * $.SECOND))) {
QR.cooldown.unset(start); QR.cooldown.unset(start);
@ -5393,7 +5403,7 @@
} }
if (seconds && Conf['Cooldown Prediction'] && hasFile && upSpd) { if (seconds && Conf['Cooldown Prediction'] && hasFile && upSpd) {
seconds -= Math.floor(post.file.size / upSpd * upSpdAccuracy); seconds -= Math.floor(post.file.size / upSpd * upSpdAccuracy);
seconds = Math.max(seconds, 0); seconds = seconds > 0 ? seconds : 0;
} }
update = seconds !== null || !!QR.cooldown.seconds; update = seconds !== null || !!QR.cooldown.seconds;
QR.cooldown.seconds = 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.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.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 : ''; _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) { if (QR.selected === _this) {
return _this.load(); return _this.load();
} }
@ -5646,7 +5659,7 @@
}; };
_Class.prototype.lock = function(lock) { _Class.prototype.lock = function(lock) {
var name, _i, _len, _ref; var name, node, _i, _len, _ref;
if (lock == null) { if (lock == null) {
lock = true; lock = true;
@ -5655,10 +5668,12 @@
if (this !== QR.selected) { if (this !== QR.selected) {
return; 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++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
name = _ref[_i]; name = _ref[_i];
QR.nodes[name].disabled = lock; if (node = QR.nodes[name]) {
node.disabled = lock;
}
} }
this.nodes.rm.style.visibility = lock ? 'hidden' : ''; this.nodes.rm.style.visibility = lock ? 'hidden' : '';
(lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput); (lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput);
@ -5688,12 +5703,15 @@
}; };
_Class.prototype.load = function() { _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++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
name = _ref[_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(); this.showFileData();
return QR.characterCount(); return QR.characterCount();
@ -5707,7 +5725,7 @@
return; return;
} }
name = input.dataset.name; name = input.dataset.name;
this[name] = input.value; this[name] = input.value || input.dataset["default"] || null;
switch (name) { switch (name) {
case 'thread': case 'thread':
return QR.status(); return QR.status();
@ -5731,15 +5749,18 @@
}; };
_Class.prototype.forceSave = function() { _Class.prototype.forceSave = function() {
var name, _i, _len, _ref; var name, node, _i, _len, _ref;
if (this !== QR.selected) { if (this !== QR.selected) {
return; 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++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
name = _ref[_i]; name = _ref[_i];
this.save(QR.nodes[name]); if (!(node = QR.nodes[name])) {
continue;
}
this.save(node);
} }
}; };
@ -6077,7 +6098,7 @@
} }
}, },
dialog: function() { 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 = { QR.nodes = nodes = {
el: dialog = UI.dialog('qr', 'top:0;right:0;', " <div class=move><label><input type=checkbox id=autohide title=Auto-hide>\n Quick Reply\n</label><a href=javascript:; class=close title=Close>×</a><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><form><div class=persona><input name=name data-name=name list=\"list-name\" placeholder=Name class=field size=1 tabindex=10><input name=email data-name=email list=\"list-email\" placeholder=E-mail class=field size=1 tabindex=20><input name=sub data-name=sub list=\"list-sub\" placeholder=Subject class=field size=1 tabindex=30></div><div class=textarea><textarea data-name=com placeholder=Comment class=field tabindex=40></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\" tabindex=50>+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=60><span id=qr-no-file>No selected file</span><input id=\"qr-filename\" data-name=\"filename\" spellcheck=\"false\"><span id=qr-extras-container><a id=qr-filerm href=javascript:; title='Remove file'>×</a><a id=dump-button title='Dump list'>+</a></span></span><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=70></label><input type=submit tabindex=80></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist>") el: dialog = UI.dialog('qr', 'top:0;right:0;', " <div class=move><label><input type=checkbox id=autohide title=Auto-hide>\n Quick Reply\n</label><a href=javascript:; class=close title=Close>×</a><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><form><div class=persona><input name=name data-name=name list=\"list-name\" placeholder=Name class=field size=1 tabindex=10><input name=email data-name=email list=\"list-email\" placeholder=E-mail class=field size=1 tabindex=20><input name=sub data-name=sub list=\"list-sub\" placeholder=Subject class=field size=1 tabindex=30></div><div class=textarea><textarea data-name=com placeholder=Comment class=field tabindex=40></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\" tabindex=50>+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=60><span id=qr-no-file>No selected file</span><input id=\"qr-filename\" data-name=\"filename\" spellcheck=\"false\"><span id=qr-extras-container><a id=qr-filerm href=javascript:; title='Remove file'>×</a><a id=dump-button title='Dump list'>+</a></span></span><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=70></label><input type=submit tabindex=80></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist>")
@ -6133,8 +6154,15 @@
name: 'filetag', name: 'filetag',
innerHTML: "<option value=0>Hentai</option>\n<option value=6>Porn</option>\n<option value=1>Japanese</option>\n<option value=2>Anime</option>\n<option value=3>Game</option>\n<option value=5>Loop</option>\n<option value=4 selected>Other</option>" innerHTML: "<option value=0>Hentai</option>\n<option value=6>Porn</option>\n<option value=1>Japanese</option>\n<option value=2>Anime</option>\n<option value=3>Game</option>\n<option value=5>Loop</option>\n<option value=4 selected>Other</option>"
}); });
nodes.flashTag.dataset["default"] = '4';
$.add(nodes.form, nodes.flashTag); $.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) { for (thread in g.BOARD.threads) {
$.add(nodes.thread, $.el('option', { $.add(nodes.thread, $.el('option', {
value: thread, value: thread,
@ -6169,16 +6197,18 @@
return QR.selected.nodes.spoiler.click(); return QR.selected.nodes.spoiler.click();
}); });
$.on(nodes.fileInput, 'change', QR.handleFiles); $.on(nodes.fileInput, 'change', QR.handleFiles);
items = ['name', 'email', 'sub', 'com', 'filename']; items = ['name', 'email', 'sub', 'com', 'filename', 'flag'];
i = 0; i = 0;
while (name = items[i++]) { save = function() {
$.on(nodes[name], 'input', function() {
return QR.selected.save(this);
});
}
$.on(nodes.thread, 'change', function() {
return QR.selected.save(this); 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']) { if (Conf['Remember QR Size']) {
$.get('QR Size', '', function(item) { $.get('QR Size', '', function(item) {
return nodes.com.style.cssText = item['QR Size']; return nodes.com.style.cssText = item['QR Size'];
@ -6200,7 +6230,7 @@
}, },
preSubmitHooks: [], preSubmitHooks: [],
submit: function(e) { 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) { if (e != null) {
e.preventDefault(); e.preventDefault();
@ -6264,7 +6294,7 @@
d.activeElement.blur(); d.activeElement.blur();
} }
post.lock(); post.lock();
postData = { formData = {
resto: threadID, resto: threadID,
name: post.name, name: post.name,
email: post.email, email: post.email,
@ -6273,6 +6303,7 @@
upfile: post.file, upfile: post.file,
filetag: filetag, filetag: filetag,
spoiler: post.spoiler, spoiler: post.spoiler,
flag: post.flag,
textonly: textOnly, textonly: textOnly,
mode: 'regist', mode: 'regist',
pwd: QR.persona.pwd, pwd: QR.persona.pwd,
@ -6294,7 +6325,7 @@
} }
}; };
extra = { extra = {
form: $.formData(postData), form: $.formData(formData),
upCallbacks: { upCallbacks: {
onload: function() { onload: function() {
QR.req.isUploadFinished = true; QR.req.isUploadFinished = true;
@ -6378,8 +6409,8 @@
threadID: threadID, threadID: threadID,
postID: postID postID: postID
}); });
postsCount = QR.posts.length; postsCount = QR.posts.length - 1;
QR.cooldown.auto = postsCount > 1 && isReply; QR.cooldown.auto = postsCount && isReply;
if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) { if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) {
notif = new Notification('Quick reply warning', { notif = new Notification('Quick reply warning', {
body: "You are running low on cached captchas. Cache count: " + captchasCount + ".", body: "You are running low on cached captchas. Cache count: " + captchasCount + ".",
@ -7494,7 +7525,7 @@
return; return;
} }
DeleteLink.cooldown.counting = post; DeleteLink.cooldown.counting = post;
length = 30; length = 60;
seconds = Math.ceil((length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND); seconds = Math.ceil((length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND);
return DeleteLink.cooldown.count(post, seconds, length, node); return DeleteLink.cooldown.count(post, seconds, length, node);
}, },
@ -7808,7 +7839,6 @@
if (g.VIEW !== 'thread' || !Conf['Thread Updater']) { if (g.VIEW !== 'thread' || !Conf['Thread Updater']) {
return; return;
} }
checked = Conf['Auto Update'] ? 'checked' : '';
if (Conf['Updater and Stats in Header']) { if (Conf['Updater and Stats in Header']) {
this.dialog = sc = $.el('span', { this.dialog = sc = $.el('span', {
innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>", innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>",
@ -7828,6 +7858,7 @@
this.checkPostCount = 0; this.checkPostCount = 0;
this.timer = $('#update-timer', sc); this.timer = $('#update-timer', sc);
this.status = $('#update-status', sc); this.status = $('#update-status', sc);
this.isUpdating = Conf['Auto Update'];
$.on(this.timer, 'click', ThreadUpdater.update); $.on(this.timer, 'click', ThreadUpdater.update);
$.on(this.status, 'click', ThreadUpdater.update); $.on(this.status, 'click', ThreadUpdater.update);
subEntries = []; subEntries = [];
@ -7845,7 +7876,7 @@
$.on(input, 'change', ThreadUpdater.cb.scrollBG); $.on(input, 'change', ThreadUpdater.cb.scrollBG);
ThreadUpdater.cb.scrollBG(); ThreadUpdater.cb.scrollBG();
} else if (input.name === 'Auto Update') { } else if (input.name === 'Auto Update') {
$.on(input, 'change', ThreadUpdater.update); $.on(input, 'change', ThreadUpdater.cb.update);
} }
subEntries.push({ subEntries.push({
el: el el: el
@ -7875,7 +7906,6 @@
ThreadUpdater.thread = this; ThreadUpdater.thread = this;
ThreadUpdater.root = this.OP.nodes.root.parentNode; ThreadUpdater.root = this.OP.nodes.root.parentNode;
ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]; ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0];
ThreadUpdater.outdateCount = 0;
ThreadUpdater.cb.interval.call($.el('input', { ThreadUpdater.cb.interval.call($.el('input', {
value: Conf['Interval'] 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', 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: { cb: {
online: function() { online: function() {
if (ThreadUpdater.online = navigator.onLine) { if (navigator.onLine) {
ThreadUpdater.outdateCount = 0; ThreadUpdater.outdateCount = 0;
ThreadUpdater.setInterval(); ThreadUpdater.setInterval();
ThreadUpdater.update();
ThreadUpdater.set('status', null, null); ThreadUpdater.set('status', null, null);
} else { } else {
ThreadUpdater.set('timer', null); ThreadUpdater.set('timer', null);
ThreadUpdater.set('status', 'Offline', 'warning'); ThreadUpdater.set('status', 'Offline', 'warning');
} }
return ThreadUpdater.cb.autoUpdate(); return ThreadUpdater.count(true);
}, },
post: function(e) { post: function(e) {
if (e.detail.threadID !== ThreadUpdater.thread.ID) { if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) {
return; return;
} }
ThreadUpdater.outdateCount = 0; ThreadUpdater.outdateCount = 0;
@ -7934,9 +7963,7 @@
return; return;
} }
ThreadUpdater.outdateCount = 0; ThreadUpdater.outdateCount = 0;
if (ThreadUpdater.seconds > ThreadUpdater.interval) { return ThreadUpdater.seconds = Math.min(ThreadUpdater.seconds, ThreadUpdater.interval);
return ThreadUpdater.setInterval();
}
}, },
scrollBG: function() { scrollBG: function() {
return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() { return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() {
@ -7945,17 +7972,10 @@
return !d.hidden; return !d.hidden;
}; };
}, },
autoUpdate: function() {
if (ThreadUpdater.online) {
return ThreadUpdater.timeout();
} else {
return clearTimeout(ThreadUpdater.timeoutID);
}
},
interval: function() { interval: function() {
var val; var val;
val = +this.value; val = parseInt(this.value, 10);
if (val < 1) { if (val < 1) {
val = 1; val = 1;
} }
@ -7966,9 +7986,9 @@
var klass, req, text, _ref; var klass, req, text, _ref;
req = ThreadUpdater.req; req = ThreadUpdater.req;
delete ThreadUpdater.req;
if (e.type !== 'loadend') { if (e.type !== 'loadend') {
req.onloadend = null; req.onloadend = null;
delete ThreadUpdater.req;
if (e.type === 'timeout') { if (e.type === 'timeout') {
ThreadUpdater.set('status', 'Retrying', null); ThreadUpdater.set('status', 'Retrying', null);
ThreadUpdater.update(); ThreadUpdater.update();
@ -7998,9 +8018,8 @@
ThreadUpdater.set('status', text, klass); ThreadUpdater.set('status', text, klass);
} }
if (ThreadUpdater.postID) { if (ThreadUpdater.postID) {
ThreadUpdater.cb.checkpost(); return ThreadUpdater.cb.checkpost();
} }
return delete ThreadUpdater.req;
} }
}, },
setInterval: function() { 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.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++); ThreadUpdater.set('timer', ThreadUpdater.seconds++);
clearTimeout(ThreadUpdater.timeoutID); return ThreadUpdater.count(true);
return ThreadUpdater.timeout();
}, },
intervalShortcut: function() { intervalShortcut: function() {
var settings; var settings;
@ -8036,20 +8054,29 @@
return el.className = klass; return el.className = klass;
} }
}, },
count: function(start) {
clearTimeout(ThreadUpdater.timeoutID);
if (start && ThreadUpdater.isUpdating && navigator.onLine) {
return ThreadUpdater.timeout();
}
},
timeout: function() { timeout: function() {
var sec;
ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000);
ThreadUpdater.set('timer', --ThreadUpdater.seconds); sec = ThreadUpdater.seconds--;
if (ThreadUpdater.seconds <= 0) { ThreadUpdater.set('timer', sec);
if (sec <= 0) {
return ThreadUpdater.update(); return ThreadUpdater.update();
} }
}, },
update: function() { update: function() {
var url; var url;
if (!ThreadUpdater.online) { if (!navigator.onLine) {
return; return;
} }
clearTimeout(ThreadUpdater.timeoutID); ThreadUpdater.count();
if (Conf['Auto Update']) { if (Conf['Auto Update']) {
ThreadUpdater.set('timer', '...'); ThreadUpdater.set('timer', '...');
} else { } else {
@ -8235,27 +8262,6 @@
ThreadWatcher.fetchAllStatus(); ThreadWatcher.fetchAllStatus();
this.db.save(); 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({ return Thread.prototype.callbacks.push({
name: 'Thread Watcher', name: 'Thread Watcher',
cb: this.node cb: this.node

View File

@ -5266,7 +5266,8 @@
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 sub: Conf['Remember Subject'] ? post.sub : void 0,
flag: post.flag
}; };
return $.set('QR.persona', persona); return $.set('QR.persona', persona);
}); });
@ -5330,12 +5331,11 @@
delay: delay delay: delay
}; };
} else { } else {
if (post.file) { if (hasFile = !!post.file) {
upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND); upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND);
QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2; QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2;
QR.cooldown.upSpd = upSpd; QR.cooldown.upSpd = upSpd;
} }
hasFile = !!post.file;
cooldown = { cooldown = {
isReply: isReply, isReply: isReply,
hasFile: hasFile, hasFile: hasFile,
@ -5392,7 +5392,17 @@
if (elapsed < 0) { if (elapsed < 0) {
continue; 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); maxTimer = Math.max(types[type] || 0, types[type + '_intra'] || 0);
if (!((start <= now && now <= start + maxTimer * $.SECOND))) { if (!((start <= now && now <= start + maxTimer * $.SECOND))) {
QR.cooldown.unset(start); QR.cooldown.unset(start);
@ -5405,7 +5415,7 @@
} }
if (seconds && Conf['Cooldown Prediction'] && hasFile && upSpd) { if (seconds && Conf['Cooldown Prediction'] && hasFile && upSpd) {
seconds -= Math.floor(post.file.size / upSpd * upSpdAccuracy); seconds -= Math.floor(post.file.size / upSpd * upSpdAccuracy);
seconds = Math.max(seconds, 0); seconds = seconds > 0 ? seconds : 0;
} }
update = seconds !== null || !!QR.cooldown.seconds; update = seconds !== null || !!QR.cooldown.seconds;
QR.cooldown.seconds = 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.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.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 : ''; _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) { if (QR.selected === _this) {
return _this.load(); return _this.load();
} }
@ -5652,7 +5665,7 @@
}; };
_Class.prototype.lock = function(lock) { _Class.prototype.lock = function(lock) {
var name, _i, _len, _ref; var name, node, _i, _len, _ref;
if (lock == null) { if (lock == null) {
lock = true; lock = true;
@ -5661,10 +5674,12 @@
if (this !== QR.selected) { if (this !== QR.selected) {
return; 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++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
name = _ref[_i]; name = _ref[_i];
QR.nodes[name].disabled = lock; if (node = QR.nodes[name]) {
node.disabled = lock;
}
} }
this.nodes.rm.style.visibility = lock ? 'hidden' : ''; this.nodes.rm.style.visibility = lock ? 'hidden' : '';
(lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput); (lock ? $.off : $.on)(QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput);
@ -5694,12 +5709,15 @@
}; };
_Class.prototype.load = function() { _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++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
name = _ref[_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(); this.showFileData();
return QR.characterCount(); return QR.characterCount();
@ -5713,7 +5731,7 @@
return; return;
} }
name = input.dataset.name; name = input.dataset.name;
this[name] = input.value; this[name] = input.value || input.dataset["default"] || null;
switch (name) { switch (name) {
case 'thread': case 'thread':
return QR.status(); return QR.status();
@ -5737,15 +5755,18 @@
}; };
_Class.prototype.forceSave = function() { _Class.prototype.forceSave = function() {
var name, _i, _len, _ref; var name, node, _i, _len, _ref;
if (this !== QR.selected) { if (this !== QR.selected) {
return; 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++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
name = _ref[_i]; name = _ref[_i];
this.save(QR.nodes[name]); if (!(node = QR.nodes[name])) {
continue;
}
this.save(node);
} }
}; };
@ -6081,7 +6102,7 @@
} }
}, },
dialog: function() { 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 = { QR.nodes = nodes = {
el: dialog = UI.dialog('qr', 'top:0;right:0;', " <div class=move><label><input type=checkbox id=autohide title=Auto-hide>\n Quick Reply\n</label><a href=javascript:; class=close title=Close>×</a><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><form><div class=persona><input name=name data-name=name list=\"list-name\" placeholder=Name class=field size=1 tabindex=10><input name=email data-name=email list=\"list-email\" placeholder=E-mail class=field size=1 tabindex=20><input name=sub data-name=sub list=\"list-sub\" placeholder=Subject class=field size=1 tabindex=30></div><div class=textarea><textarea data-name=com placeholder=Comment class=field tabindex=40></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\" tabindex=50>+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=60><span id=qr-no-file>No selected file</span><input id=\"qr-filename\" data-name=\"filename\" spellcheck=\"false\"><span id=qr-extras-container><a id=qr-filerm href=javascript:; title='Remove file'>×</a><a id=dump-button title='Dump list'>+</a></span></span><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=70></label><input type=submit tabindex=80></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist>") el: dialog = UI.dialog('qr', 'top:0;right:0;', " <div class=move><label><input type=checkbox id=autohide title=Auto-hide>\n Quick Reply\n</label><a href=javascript:; class=close title=Close>×</a><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><form><div class=persona><input name=name data-name=name list=\"list-name\" placeholder=Name class=field size=1 tabindex=10><input name=email data-name=email list=\"list-email\" placeholder=E-mail class=field size=1 tabindex=20><input name=sub data-name=sub list=\"list-sub\" placeholder=Subject class=field size=1 tabindex=30></div><div class=textarea><textarea data-name=com placeholder=Comment class=field tabindex=40></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\" tabindex=50>+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=60><span id=qr-no-file>No selected file</span><input id=\"qr-filename\" data-name=\"filename\" spellcheck=\"false\"><span id=qr-extras-container><a id=qr-filerm href=javascript:; title='Remove file'>×</a><a id=dump-button title='Dump list'>+</a></span></span><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=70></label><input type=submit tabindex=80></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist>")
@ -6137,8 +6158,15 @@
name: 'filetag', name: 'filetag',
innerHTML: "<option value=0>Hentai</option>\n<option value=6>Porn</option>\n<option value=1>Japanese</option>\n<option value=2>Anime</option>\n<option value=3>Game</option>\n<option value=5>Loop</option>\n<option value=4 selected>Other</option>" innerHTML: "<option value=0>Hentai</option>\n<option value=6>Porn</option>\n<option value=1>Japanese</option>\n<option value=2>Anime</option>\n<option value=3>Game</option>\n<option value=5>Loop</option>\n<option value=4 selected>Other</option>"
}); });
nodes.flashTag.dataset["default"] = '4';
$.add(nodes.form, nodes.flashTag); $.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) { for (thread in g.BOARD.threads) {
$.add(nodes.thread, $.el('option', { $.add(nodes.thread, $.el('option', {
value: thread, value: thread,
@ -6167,16 +6195,18 @@
return QR.selected.nodes.spoiler.click(); return QR.selected.nodes.spoiler.click();
}); });
$.on(nodes.fileInput, 'change', QR.handleFiles); $.on(nodes.fileInput, 'change', QR.handleFiles);
items = ['name', 'email', 'sub', 'com', 'filename']; items = ['name', 'email', 'sub', 'com', 'filename', 'flag'];
i = 0; i = 0;
while (name = items[i++]) { save = function() {
$.on(nodes[name], 'input', function() {
return QR.selected.save(this);
});
}
$.on(nodes.thread, 'change', function() {
return QR.selected.save(this); 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(); QR.persona.init();
new QR.post(true); new QR.post(true);
QR.status(); QR.status();
@ -6187,7 +6217,7 @@
}, },
preSubmitHooks: [], preSubmitHooks: [],
submit: function(e) { 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) { if (e != null) {
e.preventDefault(); e.preventDefault();
@ -6251,7 +6281,7 @@
d.activeElement.blur(); d.activeElement.blur();
} }
post.lock(); post.lock();
postData = { formData = {
resto: threadID, resto: threadID,
name: post.name, name: post.name,
email: post.email, email: post.email,
@ -6260,6 +6290,7 @@
upfile: post.file, upfile: post.file,
filetag: filetag, filetag: filetag,
spoiler: post.spoiler, spoiler: post.spoiler,
flag: post.flag,
textonly: textOnly, textonly: textOnly,
mode: 'regist', mode: 'regist',
pwd: QR.persona.pwd, pwd: QR.persona.pwd,
@ -6281,7 +6312,7 @@
} }
}; };
extra = { extra = {
form: $.formData(postData), form: $.formData(formData),
upCallbacks: { upCallbacks: {
onload: function() { onload: function() {
QR.req.isUploadFinished = true; QR.req.isUploadFinished = true;
@ -6365,8 +6396,8 @@
threadID: threadID, threadID: threadID,
postID: postID postID: postID
}); });
postsCount = QR.posts.length; postsCount = QR.posts.length - 1;
QR.cooldown.auto = postsCount > 1 && isReply; QR.cooldown.auto = postsCount && isReply;
if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) { if (QR.cooldown.auto && QR.captcha.isEnabled && (captchasCount = QR.captcha.captchas.length) < 3 && captchasCount < postsCount) {
notif = new Notification('Quick reply warning', { notif = new Notification('Quick reply warning', {
body: "You are running low on cached captchas. Cache count: " + captchasCount + ".", body: "You are running low on cached captchas. Cache count: " + captchasCount + ".",
@ -7481,7 +7512,7 @@
return; return;
} }
DeleteLink.cooldown.counting = post; DeleteLink.cooldown.counting = post;
length = 30; length = 60;
seconds = Math.ceil((length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND); seconds = Math.ceil((length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND);
return DeleteLink.cooldown.count(post, seconds, length, node); return DeleteLink.cooldown.count(post, seconds, length, node);
}, },
@ -7795,7 +7826,6 @@
if (g.VIEW !== 'thread' || !Conf['Thread Updater']) { if (g.VIEW !== 'thread' || !Conf['Thread Updater']) {
return; return;
} }
checked = Conf['Auto Update'] ? 'checked' : '';
if (Conf['Updater and Stats in Header']) { if (Conf['Updater and Stats in Header']) {
this.dialog = sc = $.el('span', { this.dialog = sc = $.el('span', {
innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>", innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>",
@ -7815,6 +7845,7 @@
this.checkPostCount = 0; this.checkPostCount = 0;
this.timer = $('#update-timer', sc); this.timer = $('#update-timer', sc);
this.status = $('#update-status', sc); this.status = $('#update-status', sc);
this.isUpdating = Conf['Auto Update'];
$.on(this.timer, 'click', ThreadUpdater.update); $.on(this.timer, 'click', ThreadUpdater.update);
$.on(this.status, 'click', ThreadUpdater.update); $.on(this.status, 'click', ThreadUpdater.update);
subEntries = []; subEntries = [];
@ -7832,7 +7863,7 @@
$.on(input, 'change', ThreadUpdater.cb.scrollBG); $.on(input, 'change', ThreadUpdater.cb.scrollBG);
ThreadUpdater.cb.scrollBG(); ThreadUpdater.cb.scrollBG();
} else if (input.name === 'Auto Update') { } else if (input.name === 'Auto Update') {
$.on(input, 'change', ThreadUpdater.update); $.on(input, 'change', ThreadUpdater.cb.update);
} }
subEntries.push({ subEntries.push({
el: el el: el
@ -7862,7 +7893,6 @@
ThreadUpdater.thread = this; ThreadUpdater.thread = this;
ThreadUpdater.root = this.OP.nodes.root.parentNode; ThreadUpdater.root = this.OP.nodes.root.parentNode;
ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]; ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0];
ThreadUpdater.outdateCount = 0;
ThreadUpdater.cb.interval.call($.el('input', { ThreadUpdater.cb.interval.call($.el('input', {
value: Conf['Interval'] 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', 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: { cb: {
online: function() { online: function() {
if (ThreadUpdater.online = navigator.onLine) { if (navigator.onLine) {
ThreadUpdater.outdateCount = 0; ThreadUpdater.outdateCount = 0;
ThreadUpdater.setInterval(); ThreadUpdater.setInterval();
ThreadUpdater.update();
ThreadUpdater.set('status', null, null); ThreadUpdater.set('status', null, null);
} else { } else {
ThreadUpdater.set('timer', null); ThreadUpdater.set('timer', null);
ThreadUpdater.set('status', 'Offline', 'warning'); ThreadUpdater.set('status', 'Offline', 'warning');
} }
return ThreadUpdater.cb.autoUpdate(); return ThreadUpdater.count(true);
}, },
post: function(e) { post: function(e) {
if (e.detail.threadID !== ThreadUpdater.thread.ID) { if (!(ThreadUpdater.isUpdating && e.detail.threadID === ThreadUpdater.thread.ID)) {
return; return;
} }
ThreadUpdater.outdateCount = 0; ThreadUpdater.outdateCount = 0;
@ -7921,9 +7950,7 @@
return; return;
} }
ThreadUpdater.outdateCount = 0; ThreadUpdater.outdateCount = 0;
if (ThreadUpdater.seconds > ThreadUpdater.interval) { return ThreadUpdater.seconds = Math.min(ThreadUpdater.seconds, ThreadUpdater.interval);
return ThreadUpdater.setInterval();
}
}, },
scrollBG: function() { scrollBG: function() {
return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() { return ThreadUpdater.scrollBG = Conf['Scroll BG'] ? function() {
@ -7932,17 +7959,10 @@
return !d.hidden; return !d.hidden;
}; };
}, },
autoUpdate: function() {
if (ThreadUpdater.online) {
return ThreadUpdater.timeout();
} else {
return clearTimeout(ThreadUpdater.timeoutID);
}
},
interval: function() { interval: function() {
var val; var val;
val = +this.value; val = parseInt(this.value, 10);
if (val < 1) { if (val < 1) {
val = 1; val = 1;
} }
@ -7953,9 +7973,9 @@
var klass, req, text, _ref; var klass, req, text, _ref;
req = ThreadUpdater.req; req = ThreadUpdater.req;
delete ThreadUpdater.req;
if (e.type !== 'loadend') { if (e.type !== 'loadend') {
req.onloadend = null; req.onloadend = null;
delete ThreadUpdater.req;
if (e.type === 'timeout') { if (e.type === 'timeout') {
ThreadUpdater.set('status', 'Retrying', null); ThreadUpdater.set('status', 'Retrying', null);
ThreadUpdater.update(); ThreadUpdater.update();
@ -7985,9 +8005,8 @@
ThreadUpdater.set('status', text, klass); ThreadUpdater.set('status', text, klass);
} }
if (ThreadUpdater.postID) { if (ThreadUpdater.postID) {
ThreadUpdater.cb.checkpost(); return ThreadUpdater.cb.checkpost();
} }
return delete ThreadUpdater.req;
} }
}, },
setInterval: function() { 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.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++); ThreadUpdater.set('timer', ThreadUpdater.seconds++);
clearTimeout(ThreadUpdater.timeoutID); return ThreadUpdater.count(true);
return ThreadUpdater.timeout();
}, },
intervalShortcut: function() { intervalShortcut: function() {
var settings; var settings;
@ -8023,20 +8041,29 @@
return el.className = klass; return el.className = klass;
} }
}, },
count: function(start) {
clearTimeout(ThreadUpdater.timeoutID);
if (start && ThreadUpdater.isUpdating && navigator.onLine) {
return ThreadUpdater.timeout();
}
},
timeout: function() { timeout: function() {
var sec;
ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000);
ThreadUpdater.set('timer', --ThreadUpdater.seconds); sec = ThreadUpdater.seconds--;
if (ThreadUpdater.seconds <= 0) { ThreadUpdater.set('timer', sec);
if (sec <= 0) {
return ThreadUpdater.update(); return ThreadUpdater.update();
} }
}, },
update: function() { update: function() {
var url; var url;
if (!ThreadUpdater.online) { if (!navigator.onLine) {
return; return;
} }
clearTimeout(ThreadUpdater.timeoutID); ThreadUpdater.count();
if (Conf['Auto Update']) { if (Conf['Auto Update']) {
ThreadUpdater.set('timer', '...'); ThreadUpdater.set('timer', '...');
} else { } else {
@ -8222,27 +8249,6 @@
ThreadWatcher.fetchAllStatus(); ThreadWatcher.fetchAllStatus();
this.db.save(); 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({ return Thread.prototype.callbacks.push({
name: 'Thread Watcher', name: 'Thread Watcher',
cb: this.node cb: this.node

View File

@ -581,7 +581,7 @@ a.hide-announcement {
align-self: stretch; align-self: stretch;
flex: 1; flex: 1;
} }
#qr select { #qr select[data-name=thread] {
margin: 0; margin: 0;
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
@ -794,7 +794,6 @@ a.hide-announcement {
} }
#file-n-submit-container input { #file-n-submit-container input {
margin: 0; margin: 0;
padding: 0;
} }
#file-n-submit input[type='submit'] { #file-n-submit input[type='submit'] {
order: 1; order: 1;

110
json/archives.json Normal file
View File

@ -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"]
}]

View File

@ -23,14 +23,14 @@
"font-awesome": "git://github.com/MayhemYDG/Font-Awesome.git#df4285951124f9ca1f3907438462e5ba9e464bcb", "font-awesome": "git://github.com/MayhemYDG/Font-Awesome.git#df4285951124f9ca1f3907438462e5ba9e464bcb",
"grunt": "~0.4.1", "grunt": "~0.4.1",
"grunt-bump": "~0.0.11", "grunt-bump": "~0.0.11",
"grunt-concurrent": "~0.3.0", "grunt-concurrent": "~0.3.1",
"grunt-contrib-clean": "~0.5.0", "grunt-contrib-clean": "~0.5.0",
"grunt-contrib-coffee": "~0.7.0", "grunt-contrib-coffee": "~0.7.0",
"grunt-contrib-compress": "~0.5.2", "grunt-contrib-compress": "~0.5.2",
"grunt-contrib-concat": "~0.3.0", "grunt-contrib-concat": "~0.3.0",
"grunt-contrib-copy": "~0.4.1", "grunt-contrib-copy": "~0.4.1",
"grunt-contrib-watch": "~0.5.0", "grunt-contrib-watch": "~0.5.3",
"grunt-shell": "~0.3.1", "grunt-shell": "~0.4.0",
"load-grunt-tasks": "~0.1.0" "load-grunt-tasks": "~0.1.0"
}, },
"repository": { "repository": {

View File

@ -15,4 +15,4 @@ Anonymize =
delete @nodes.tripcode delete @nodes.tripcode
if @info.email if @info.email
$.replace email, name $.replace email, name
delete @nodes.email delete @nodes.email

View File

@ -453,9 +453,8 @@ Config =
#/Mod$/;highlight:mod;op:yes #/Mod$/;highlight:mod;op:yes
# Set a custom class for moot: # Set a custom class for moot:
#/Admin$/;highlight:moot;op:yes #/Admin$/;highlight:moot;op:yes
""" """
email: ""
email: ""
subject: """ subject: """
# Filter Generals on /v/: # Filter Generals on /v/:
#/general/i;boards:v;op:only #/general/i;boards:v;op:only

View File

@ -84,7 +84,7 @@ DeleteLink =
delete DeleteLink.cooldown.counting delete DeleteLink.cooldown.counting
return return
DeleteLink.cooldown.counting = post DeleteLink.cooldown.counting = post
length = 30 length = 60
seconds = Math.ceil (length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND seconds = Math.ceil (length * $.SECOND - (Date.now() - post.info.date)) / $.SECOND
DeleteLink.cooldown.count post, seconds, length, node DeleteLink.cooldown.count post, seconds, length, node
count: (post, seconds, length, node) -> count: (post, seconds, length, node) ->

View File

@ -2,8 +2,6 @@ ThreadUpdater =
init: -> init: ->
return if g.VIEW isnt 'thread' or !Conf['Thread Updater'] 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'] if Conf['Updater and Stats in Header']
@dialog = sc = $.el 'span', @dialog = sc = $.el 'span',
innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>" innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>"
@ -16,12 +14,13 @@ ThreadUpdater =
$.addClass doc, 'float' $.addClass doc, 'float'
$.ready => $.ready =>
$.addClass doc, 'float' $.addClass doc, 'float'
$.add d.body, sc $.add d.body, sc
@checkPostCount = 0 @checkPostCount = 0
@timer = $ '#update-timer', sc @timer = $ '#update-timer', sc
@status = $ '#update-status', sc @status = $ '#update-status', sc
@isUpdating = Conf['Auto Update']
$.on @timer, 'click', ThreadUpdater.update $.on @timer, 'click', ThreadUpdater.update
$.on @status, 'click', ThreadUpdater.update $.on @status, 'click', ThreadUpdater.update
@ -38,7 +37,7 @@ ThreadUpdater =
$.on input, 'change', ThreadUpdater.cb.scrollBG $.on input, 'change', ThreadUpdater.cb.scrollBG
ThreadUpdater.cb.scrollBG() ThreadUpdater.cb.scrollBG()
else if input.name is 'Auto Update' else if input.name is 'Auto Update'
$.on input, 'change', ThreadUpdater.update $.on input, 'change', ThreadUpdater.cb.update
subEntries.push el: el subEntries.push el: el
settings = $.el 'span', settings = $.el 'span',
@ -63,7 +62,6 @@ ThreadUpdater =
ThreadUpdater.thread = @ ThreadUpdater.thread = @
ThreadUpdater.root = @OP.nodes.root.parentNode ThreadUpdater.root = @OP.nodes.root.parentNode
ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0] ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]
ThreadUpdater.outdateCount = 0
ThreadUpdater.cb.interval.call $.el 'input', value: Conf['Interval'] ThreadUpdater.cb.interval.call $.el 'input', value: Conf['Interval']
@ -81,17 +79,16 @@ ThreadUpdater =
cb: cb:
online: -> online: ->
if ThreadUpdater.online = navigator.onLine if navigator.onLine
ThreadUpdater.outdateCount = 0 ThreadUpdater.outdateCount = 0
ThreadUpdater.setInterval() ThreadUpdater.setInterval()
ThreadUpdater.update()
ThreadUpdater.set 'status', null, null ThreadUpdater.set 'status', null, null
else else
ThreadUpdater.set 'timer', null ThreadUpdater.set 'timer', null
ThreadUpdater.set 'status', 'Offline', 'warning' ThreadUpdater.set 'status', 'Offline', 'warning'
ThreadUpdater.cb.autoUpdate() ThreadUpdater.count true
post: (e) -> 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 ThreadUpdater.outdateCount = 0
setTimeout ThreadUpdater.update, 1000 if ThreadUpdater.seconds > 2 setTimeout ThreadUpdater.update, 1000 if ThreadUpdater.seconds > 2
checkpost: (e) -> checkpost: (e) ->
@ -110,28 +107,22 @@ ThreadUpdater =
return if d.hidden return if d.hidden
# Reset the counter when we focus this tab. # Reset the counter when we focus this tab.
ThreadUpdater.outdateCount = 0 ThreadUpdater.outdateCount = 0
if ThreadUpdater.seconds > ThreadUpdater.interval ThreadUpdater.seconds = Math.min ThreadUpdater.seconds, ThreadUpdater.interval
ThreadUpdater.setInterval()
scrollBG: -> scrollBG: ->
ThreadUpdater.scrollBG = if Conf['Scroll BG'] ThreadUpdater.scrollBG = if Conf['Scroll BG']
-> true -> true
else else
-> not d.hidden -> not d.hidden
autoUpdate: ->
if ThreadUpdater.online
ThreadUpdater.timeout()
else
clearTimeout ThreadUpdater.timeoutID
interval: -> interval: ->
val = +@value val = parseInt @value, 10
if val < 1 then val = 1 if val < 1 then val = 1
ThreadUpdater.interval = @value = val ThreadUpdater.interval = @value = val
$.cb.value.call @ $.cb.value.call @
load: (e) -> load: (e) ->
{req} = ThreadUpdater {req} = ThreadUpdater
delete ThreadUpdater.req
if e.type isnt 'loadend' # timeout or abort if e.type isnt 'loadend' # timeout or abort
req.onloadend = null req.onloadend = null
delete ThreadUpdater.req
if e.type is 'timeout' if e.type is 'timeout'
ThreadUpdater.set 'status', 'Retrying', null ThreadUpdater.set 'status', 'Retrying', null
ThreadUpdater.update() ThreadUpdater.update()
@ -161,8 +152,6 @@ ThreadUpdater =
if ThreadUpdater.postID if ThreadUpdater.postID
ThreadUpdater.cb.checkpost() ThreadUpdater.cb.checkpost()
delete ThreadUpdater.req
setInterval: -> setInterval: ->
i = ThreadUpdater.interval i = ThreadUpdater.interval
# Math.min/max is provably slow: http://jsperf.com/math-s-min-max-vs-homemade/5 # Math.min/max is provably slow: http://jsperf.com/math-s-min-max-vs-homemade/5
@ -176,8 +165,7 @@ ThreadUpdater =
else else
i i
ThreadUpdater.set 'timer', ThreadUpdater.seconds++ ThreadUpdater.set 'timer', ThreadUpdater.seconds++
clearTimeout ThreadUpdater.timeoutID ThreadUpdater.count true
ThreadUpdater.timeout()
intervalShortcut: -> intervalShortcut: ->
Settings.open 'Advanced' Settings.open 'Advanced'
@ -194,14 +182,19 @@ ThreadUpdater =
el.textContent = text el.textContent = text
el.className = klass if klass isnt undefined el.className = klass if klass isnt undefined
count: (start) ->
clearTimeout ThreadUpdater.timeoutID
ThreadUpdater.timeout() if start and ThreadUpdater.isUpdating and navigator.onLine
timeout: -> timeout: ->
ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000 ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000
ThreadUpdater.set 'timer', --ThreadUpdater.seconds sec = ThreadUpdater.seconds--
ThreadUpdater.update() if ThreadUpdater.seconds <= 0 ThreadUpdater.set 'timer', sec
ThreadUpdater.update() if sec <= 0
update: -> update: ->
return unless ThreadUpdater.online return unless navigator.onLine
clearTimeout ThreadUpdater.timeoutID ThreadUpdater.count()
if Conf['Auto Update'] if Conf['Auto Update']
ThreadUpdater.set 'timer', '...' ThreadUpdater.set 'timer', '...'
else else

View File

@ -31,14 +31,6 @@ ThreadWatcher =
ThreadWatcher.fetchAllStatus() ThreadWatcher.fetchAllStatus()
@db.save() @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 Thread::callbacks.push
name: 'Thread Watcher' name: 'Thread Watcher'
cb: @node cb: @node

View File

@ -268,6 +268,7 @@ QR =
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 sub: if Conf['Remember Subject'] then post.sub else undefined
flag: post.flag
$.set 'QR.persona', persona $.set 'QR.persona', persona
cooldown: cooldown:
@ -307,11 +308,10 @@ QR =
if delay if delay
cooldown = {delay} cooldown = {delay}
else else
if post.file if hasFile = !!post.file
upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND) upSpd = post.file.size / ((start - req.uploadStartTime) / $.SECOND)
QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2 QR.cooldown.upSpdAccuracy = ((upSpd > QR.cooldown.upSpd * .9) + QR.cooldown.upSpdAccuracy) / 2
QR.cooldown.upSpd = upSpd QR.cooldown.upSpd = upSpd
hasFile = !!post.file
cooldown = {isReply, hasFile, threadID} cooldown = {isReply, hasFile, threadID}
QR.cooldown.cooldowns[start] = cooldown QR.cooldown.cooldowns[start] = cooldown
$.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns $.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns
@ -358,24 +358,28 @@ QR =
if isReply is cooldown.isReply if isReply is cooldown.isReply
# Only cooldowns relevant to this post can set the seconds variable: # 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 elapsed = Math.floor (now - start) / $.SECOND
continue if elapsed < 0 # clock changed since then? continue if elapsed < 0 # clock changed since then?
type = unless isReply unless isReply
'thread' type = 'thread'
else if hasFile 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 else
'reply' type = 'reply'
maxTimer = Math.max types[type] or 0, types[type + '_intra'] or 0 maxTimer = Math.max types[type] or 0, types[type + '_intra'] or 0
unless start <= now <= start + maxTimer * $.SECOND unless start <= now <= start + maxTimer * $.SECOND
QR.cooldown.unset start 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 seconds = Math.max seconds, types[type] - elapsed
if seconds and Conf['Cooldown Prediction'] and hasFile and upSpd if seconds and Conf['Cooldown Prediction'] and hasFile and upSpd
seconds -= Math.floor post.file.size / upSpd * upSpdAccuracy 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. # 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.
@ -565,6 +569,12 @@ QR =
if prev then prev.sub else persona.sub if prev then prev.sub else persona.sub
else else
'' ''
if QR.nodes.flag
@flag = if prev
prev.flag
else
persona.flag
@load() if QR.selected is @ # load persona @load() if QR.selected is @ # load persona
@select() if select @select() if select
@unlock() @unlock()
@ -586,8 +596,8 @@ QR =
lock: (lock=true) -> lock: (lock=true) ->
@isLocked = lock @isLocked = lock
return unless @ is QR.selected return unless @ is QR.selected
for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler'] for name in ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag'] when node = QR.nodes[name]
QR.nodes[name].disabled = lock node.disabled = lock
@nodes.rm.style.visibility = if lock then 'hidden' else '' @nodes.rm.style.visibility = if lock then 'hidden' else ''
(if lock then $.off else $.on) QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput (if lock then $.off else $.on) QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput
@nodes.spoiler.disabled = lock @nodes.spoiler.disabled = lock
@ -612,8 +622,9 @@ QR =
load: -> load: ->
# Load this post's values. # Load this post's values.
for name in ['thread', 'name', 'email', 'sub', 'com', 'filename'] for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag']
QR.nodes[name].value = @[name] or null continue unless node = QR.nodes[name]
node.value = @[name] or node.dataset.default or null
@showFileData() @showFileData()
QR.characterCount() QR.characterCount()
@ -622,7 +633,7 @@ QR =
@spoiler = input.checked @spoiler = input.checked
return return
{name} = input.dataset {name} = input.dataset
@[name] = input.value @[name] = input.value or input.dataset.default or null
switch name switch name
when 'thread' when 'thread'
QR.status() QR.status()
@ -647,8 +658,9 @@ QR =
return unless @ is QR.selected return unless @ is QR.selected
# Do this in case people use extensions # Do this in case people use extensions
# that do not trigger the `input` event. # that do not trigger the `input` event.
for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler'] for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag']
@save QR.nodes[name] continue unless node = QR.nodes[name]
@save node
return return
setFile: (@file) -> setFile: (@file) ->
@ -948,7 +960,13 @@ QR =
<option value=5>Loop</option> <option value=5>Loop</option>
<option value=4 selected>Other</option> <option value=4 selected>Other</option>
""" """
nodes.flashTag.dataset.default = '4'
$.add nodes.form, nodes.flashTag $.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. # Make a list of threads.
for thread of g.BOARD.threads for thread of g.BOARD.threads
@ -978,12 +996,15 @@ QR =
$.on nodes.fileExtras, 'click', (e) -> e.stopPropagation() $.on nodes.fileExtras, 'click', (e) -> e.stopPropagation()
$.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click() $.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click()
$.on nodes.fileInput, 'change', QR.handleFiles $.on nodes.fileInput, 'change', QR.handleFiles
# save selected post's data # save selected post's data
items = ['name', 'email', 'sub', 'com', 'filename'] items = ['name', 'email', 'sub', 'com', 'filename', 'flag']
i = 0 i = 0
save = -> QR.selected.save @
while name = items[i++] while name = items[i++]
$.on nodes[name], 'input', -> QR.selected.save @ continue unless node = nodes[name]
$.on nodes.thread, 'change', -> QR.selected.save @ event = if node.nodeName is 'SELECT' then 'change' else 'input'
$.on nodes[name], event, save
<% if (type === 'userscript') { %> <% if (type === 'userscript') { %>
if Conf['Remember QR Size'] if Conf['Remember QR Size']
@ -1029,7 +1050,7 @@ QR =
# prevent errors # prevent errors
if threadID is 'new' if threadID is 'new'
threadID = null 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.' err = 'New threads require a subject.'
else unless post.file or textOnly = !!$ 'input[name=textonly]', $.id 'postForm' else unless post.file or textOnly = !!$ 'input[name=textonly]', $.id 'postForm'
err = 'No file selected.' err = 'No file selected.'
@ -1065,7 +1086,7 @@ QR =
post.lock() post.lock()
postData = formData =
resto: threadID resto: threadID
name: post.name name: post.name
email: post.email email: post.email
@ -1074,6 +1095,7 @@ QR =
upfile: post.file upfile: post.file
filetag: filetag filetag: filetag
spoiler: post.spoiler spoiler: post.spoiler
flag: post.flag
textonly: textOnly textonly: textOnly
mode: 'regist' mode: 'regist'
pwd: QR.persona.pwd pwd: QR.persona.pwd
@ -1097,7 +1119,7 @@ QR =
[<a href="//4chan.org/banned" target=_blank>Banned?</a>] [<a href="https://github.com/seaweedchan/4chan-x/wiki/Frequently-Asked-Questions#what-does-4chan-x-encountered-an-error-while-posting-please-try-again-mean" target=_blank>More info</a>] [<a href="//4chan.org/banned" target=_blank>Banned?</a>] [<a href="https://github.com/seaweedchan/4chan-x/wiki/Frequently-Asked-Questions#what-does-4chan-x-encountered-an-error-while-posting-please-try-again-mean" target=_blank>More info</a>]
""" """
extra = extra =
form: $.formData postData form: $.formData formData
upCallbacks: upCallbacks:
onload: -> onload: ->
# Upload done, waiting for server response. # Upload done, waiting for server response.
@ -1202,9 +1224,9 @@ QR =
postID postID
} }
# Enable auto-posting if we have stuff to post, disable it otherwise. # Enable auto-posting if we have stuff left to post, disable it otherwise.
postsCount = QR.posts.length postsCount = QR.posts.length - 1
QR.cooldown.auto = postsCount > 1 and isReply QR.cooldown.auto = postsCount and isReply
if QR.cooldown.auto and QR.captcha.isEnabled and (captchasCount = QR.captcha.captchas.length) < 3 and captchasCount < postsCount if QR.cooldown.auto and QR.captcha.isEnabled and (captchasCount = QR.captcha.captchas.length) < 3 and captchasCount < postsCount
notif = new Notification 'Quick reply warning', notif = new Notification 'Quick reply warning',
body: "You are running low on cached captchas. Cache count: #{captchasCount}." body: "You are running low on cached captchas. Cache count: #{captchasCount}."