diff --git a/CHANGELOG.md b/CHANGELOG.md
index 647fa73a4..8eab756be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,27 @@
+
+**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/.
+
+**seaweedchan**:
+- Fix thread updater bug introduced in last version
+- Just some small fixes.
+
+**zixaphir**:
+- Some changes to mascots
+ * Silhouette mascots are now generated dynamically with the silhouette filter
+ * Images are now compressed client side before being uploaded via the upload interface (this is only for mascots, not posts)
+- Fix an issue with Linkifier linkifying replaced spoilers
+- Fix an issue with "fit height" in the gallery on Chrome
+- Small thread updater fixes
+
### v2.3.10
*2013-08-31*
diff --git a/Gruntfile.coffee b/Gruntfile.coffee
index 50b361e79..313210131 100755
--- a/Gruntfile.coffee
+++ b/Gruntfile.coffee
@@ -1,22 +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 +33,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 +47,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 +85,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/LICENSE b/LICENSE
index 6878ce473..28109fe2c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
/*
-* appchan x - Version 2.3.10 - 2013-09-20
+* appchan x - Version 2.3.10 - 2013-09-24
*
* Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
diff --git a/builds/appchan-x.user.js b/builds/appchan-x.user.js
index 79d981e42..7841cc544 100644
--- a/builds/appchan-x.user.js
+++ b/builds/appchan-x.user.js
@@ -20,7 +20,7 @@
// ==/UserScript==
/*
-* appchan x - Version 2.3.10 - 2013-09-20
+* appchan x - Version 2.3.10 - 2013-09-24
*
* Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
@@ -335,7 +335,7 @@
uniqueID: "# Filter a specific ID:\n#/Txhvk1Tl/",
tripcode: "# Filter any tripfag\n#/^!/",
capcode: "# Set a custom class for mods:\n#/Mod$/;highlight:mod;op:yes\n# Set a custom class for moot:\n#/Admin$/;highlight:moot;op:yes",
- email: "# Filter any e-mails that are not `sage` on /a/ and /jp/:\n#/^(?!sage$)/;boards:a,jp",
+ email: "",
subject: "# Filter Generals on /v/:\n#/general/i;boards:v;op:only",
comment: "# Filter Stallman copypasta on /g/:\n#/what you\'re refer+ing to as linux/i;boards:g",
flag: '',
@@ -3320,7 +3320,7 @@
this.nodes.comment.normalize();
bq = this.nodes.comment.cloneNode(true);
- nodes = $$('.abbr, .capcodeReplies, .exif, b', bq);
+ nodes = $$('.abbr, .exif, b', bq);
i = 0;
while (node = nodes[i++]) {
$.rm(node);
@@ -3352,7 +3352,7 @@
return;
}
this.nodes.quotelinks.push(quotelink);
- if (this.isClone || !this.isReply && $.hasClass(quotelink.parentNode.parentNode, 'capcodeReplies')) {
+ if (this.isClone) {
return;
}
fullID = "" + match[1] + "." + match[2];
@@ -4224,7 +4224,6 @@
date: data.now,
dateUTC: data.time,
comment: data.com,
- capcodeReplies: data.capcode_replies,
isSticky: !!data.sticky,
isClosed: !!data.closed
};
@@ -4252,9 +4251,9 @@
@license: https://github.com/4chan/4chan-JS/blob/master/LICENSE
*/
- var a, boardID, capcode, capcodeClass, capcodeReplies, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref;
+ var a, boardID, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref;
- postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, capcodeReplies = o.capcodeReplies, file = o.file;
+ postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file;
isOP = postID === threadID;
staticPath = '//static.4chan.org/image/';
if (email) {
@@ -4331,7 +4330,7 @@
container = $.el('div', {
id: "pc" + postID,
className: "postContainer " + (isOP ? 'op' : 'reply') + "Container",
- innerHTML: "" + (isOP ? '' : "
>>
") + "" + (name || '') + "" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "
" + subject + "" + date + "No." + postID + " " + (isOP ? fileHTML : '') + "
" + subject + "
" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "" + " " + "
" + date + "" + " " + "
No." + postID + "" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + " " + "
"
+ innerHTML: "" + (isOP ? '' : ">>
") + "" + (name || '') + "" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "
" + subject + "" + date + "No." + postID + " " + (isOP ? fileHTML : '') + "
" + subject + "
" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "" + " " + "
" + date + "" + " " + "
No." + postID + "" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + " " + "
"
});
_ref = $$('.quotelink', container);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -4342,47 +4341,7 @@
}
quote.href = "/" + boardID + "/res/" + href;
}
- Build.capcodeReplies({
- boardID: boardID,
- threadID: threadID,
- root: container,
- capcodeReplies: capcodeReplies
- });
return container;
- },
- capcodeReplies: function(_arg) {
- var array, boardID, bq, capcodeReplies, capcodeType, generateCapcodeReplies, html, root, threadID;
-
- boardID = _arg.boardID, threadID = _arg.threadID, bq = _arg.bq, root = _arg.root, capcodeReplies = _arg.capcodeReplies;
- if (!capcodeReplies) {
- return;
- }
- generateCapcodeReplies = function(capcodeType, array) {
- return "" + ((function() {
- switch (capcodeType) {
- case 'admin':
- return 'Administrator';
- case 'mod':
- return 'Moderator';
- case 'developer':
- return 'Developer';
- }
- })()) + " Repl" + (array.length > 1 ? 'ies' : 'y') + ": " + (array.map(function(ID) {
- return ">>" + ID + "";
- }).join(' ')) + "
";
- };
- html = [];
- for (capcodeType in capcodeReplies) {
- array = capcodeReplies[capcodeType];
- html.push(generateCapcodeReplies(capcodeType, array));
- }
- bq || (bq = $('blockquote', root));
- return $.add(bq, [
- $.el('br'), $.el('br'), $.el('span', {
- className: 'capcodeReplies',
- innerHTML: html.join('')
- })
- ]);
}
};
@@ -5109,12 +5068,8 @@
delete this.nodes.tripcode;
}
if (this.info.email) {
- if (/sage/i.test(this.info.email)) {
- return email.href = 'mailto:sage';
- } else {
- $.replace(email, name);
- return delete this.nodes.email;
- }
+ $.replace(email, name);
+ return delete this.nodes.email;
}
}
};
@@ -5678,7 +5633,7 @@
return;
}
a = PostHiding.makeButton(post, 'show');
- postInfo = Conf['Anonymize'] ? 'Anonymous' : post.info.name;
+ postInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', post.nodes.info).textContent;
$.add(a, $.tn(" " + postInfo));
post.nodes.stub = $.el('div', {
className: 'stub'
@@ -6026,7 +5981,7 @@
}
numReplies = ((span = $('.summary', threadRoot)) ? +span.textContent.match(/\d+/) : 0) + $$('.opContainer ~ .replyContainer', threadRoot).length;
numReplies = numReplies === 1 ? '1 reply' : "" + (numReplies || 'no') + " replies";
- opInfo = Conf['Anonymize'] ? 'Anonymous' : OP.info.name;
+ opInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', OP.nodes.info).textContent;
a = ThreadHiding.makeButton(thread, 'show');
$.add(a, $.tn(" " + opInfo + " (" + numReplies + ")"));
thread.stub = $.el('div', {
@@ -7581,7 +7536,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);
});
@@ -7589,36 +7545,30 @@
},
cooldown: {
init: function() {
- var board;
+ var key, setTimers, type, _base,
+ _this = this;
if (!Conf['Cooldown']) {
return;
}
- board = g.BOARD.ID;
- QR.cooldown.types = {
- thread: (function() {
- switch (board) {
- case 'q':
- return 86400;
- case 'b':
- case 'soc':
- case 'r9k':
- return 600;
- default:
- return 300;
- }
- })(),
- sage: board === 'q' ? 600 : 60,
- file: board === 'q' ? 300 : 30,
- post: board === 'q' ? 150 : 30
+ setTimers = function(e) {
+ return QR.cooldown.types = e.detail;
};
+ $.on(window, 'cooldown:timers', setTimers);
+ $.globalEval('window.dispatchEvent(new CustomEvent("cooldown:timers", {detail: cooldowns}))');
+ (_base = QR.cooldown).types || (_base.types = {});
+ $.off(window, 'cooldown:timers', setTimers);
+ for (type in QR.cooldown.types) {
+ QR.cooldown.types[type] = +QR.cooldown.types[type];
+ }
QR.cooldown.upSpd = 0;
QR.cooldown.upSpdAccuracy = .5;
- $.get("cooldown." + board, {}, function(item) {
- QR.cooldown.cooldowns = item["cooldown." + board];
+ key = "cooldown." + g.BOARD;
+ $.get(key, {}, function(item) {
+ QR.cooldown.cooldowns = item[key];
return QR.cooldown.start();
});
- return $.sync("cooldown." + board, QR.cooldown.sync);
+ return $.sync(key, QR.cooldown.sync);
},
start: function() {
if (!Conf['Cooldown']) {
@@ -7639,31 +7589,27 @@
return QR.cooldown.start();
},
set: function(data) {
- var cooldown, delay, hasFile, isReply, isSage, post, req, start, type, upSpd;
+ var cooldown, delay, hasFile, isReply, post, req, start, threadID, upSpd;
if (!Conf['Cooldown']) {
return;
}
- req = data.req, post = data.post, isReply = data.isReply, delay = data.delay;
+ req = data.req, post = data.post, isReply = data.isReply, threadID = data.threadID, delay = data.delay;
start = req ? req.uploadEndTime : Date.now();
if (delay) {
cooldown = {
delay: delay
};
} else {
- if (post.file) {
- upSpd = post.file.size / ((req.uploadEndTime - req.uploadStartTime) / $.SECOND);
+ 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;
}
- isSage = /sage/i.test(post.email);
- hasFile = !!post.file;
- type = !isReply ? 'thread' : isSage ? 'sage' : hasFile ? 'file' : 'post';
cooldown = {
isReply: isReply,
- isSage: isSage,
hasFile: hasFile,
- timeout: start + QR.cooldown.types[type] * $.SECOND
+ threadID: threadID
};
}
QR.cooldown.cooldowns[start] = cooldown;
@@ -7679,7 +7625,7 @@
}
},
count: function() {
- var cooldown, cooldowns, elapsed, hasFile, isReply, isSage, now, post, seconds, start, type, types, upSpd, upSpdAccuracy, update, _ref;
+ var cooldown, cooldowns, elapsed, hasFile, isReply, maxTimer, now, post, seconds, start, type, types, upSpd, upSpdAccuracy, update, _ref;
if (!Object.keys(QR.cooldown.cooldowns).length) {
$["delete"]("" + g.BOARD + ".cooldown");
@@ -7688,11 +7634,11 @@
QR.status();
return;
}
- setTimeout(QR.cooldown.count, $.SECOND);
+ clearTimeout(QR.cooldown.timeout);
+ QR.cooldown.timeout = setTimeout(QR.cooldown.count, $.SECOND);
now = Date.now();
post = QR.posts[0];
isReply = post.thread !== 'new';
- isSage = /sage/i.test(post.email);
hasFile = !!post.file;
seconds = null;
_ref = QR.cooldown, types = _ref.types, cooldowns = _ref.cooldowns, upSpd = _ref.upSpd, upSpdAccuracy = _ref.upSpdAccuracy;
@@ -7707,20 +7653,39 @@
}
continue;
}
- if (isReply === cooldown.isReply) {
- type = !isReply ? 'thread' : isSage && cooldown.isSage ? 'sage' : hasFile && cooldown.hasFile ? 'file' : 'post';
- elapsed = Math.floor((now - start) / $.SECOND);
- if (elapsed >= 0) {
- seconds = Math.max(seconds, types[type] - elapsed);
- if (Conf['Cooldown Prediction'] && hasFile && upSpd) {
- seconds -= Math.floor(post.file.size / upSpd * upSpdAccuracy);
- seconds = Math.max(seconds, 0);
- }
- }
- }
- if (!((start <= now && now <= cooldown.timeout))) {
+ if ('timeout' in cooldown) {
QR.cooldown.unset(start);
+ continue;
}
+ if (isReply === cooldown.isReply) {
+ elapsed = Math.floor((now - start) / $.SECOND);
+ if (elapsed < 0) {
+ continue;
+ }
+ 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);
+ }
+ if (isReply && +post.thread === cooldown.threadID) {
+ type += '_intra';
+ }
+ seconds = Math.max(seconds, types[type] - elapsed);
+ }
+ }
+ if (seconds && Conf['Cooldown Prediction'] && hasFile && upSpd) {
+ seconds -= Math.floor(post.file.size / upSpd * upSpdAccuracy);
+ seconds = seconds > 0 ? seconds : 0;
}
update = seconds !== null || !!QR.cooldown.seconds;
QR.cooldown.seconds = seconds;
@@ -7939,6 +7904,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();
}
@@ -7970,7 +7938,7 @@
};
_Class.prototype.lock = function(lock) {
- var name, _i, _len, _ref;
+ var name, node, _i, _len, _ref;
if (lock == null) {
lock = true;
@@ -7979,10 +7947,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);
@@ -8012,12 +7982,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;
}
QR.tripcodeHider.call(QR.nodes['name']);
this.showFileData();
@@ -8032,7 +8005,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();
@@ -8056,15 +8029,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);
}
};
@@ -8402,7 +8378,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;', "")
@@ -8462,8 +8438,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,
@@ -8506,12 +8489,17 @@
while (name = items[i++]) {
$.on(nodes[name], 'mouseover', QR.mouseover);
}
- items = ['name', 'email', 'sub', 'com', 'filename'];
+ items = ['name', 'email', 'sub', 'com', 'filename', 'flag'];
i = 0;
+ save = function() {
+ return QR.selected.save(this);
+ };
while (name = items[i++]) {
- $.on(nodes[name], 'input', function() {
- return QR.selected.save(this);
- });
+ if (!(node = nodes[name])) {
+ continue;
+ }
+ event = node.nodeName === 'SELECT' ? 'change' : 'input';
+ $.on(nodes[name], event, save);
}
$.on(nodes['name'], 'blur', QR.tripcodeHider);
$.on(nodes.thread, 'change', function() {
@@ -8552,7 +8540,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();
@@ -8575,7 +8563,7 @@
thread = g.BOARD.threads[threadID];
if (threadID === 'new') {
threadID = null;
- if (['vg', 'q'].contains(g.BOARD.ID) && !post.sub) {
+ if (g.BOARD.ID === 'vg' && !post.sub) {
err = 'New threads require a subject.';
} else if (!(post.file || (textOnly = !!$('input[name=textonly]', $.id('postForm'))))) {
err = 'No file selected.';
@@ -8616,7 +8604,7 @@
d.activeElement.blur();
}
post.lock();
- postData = {
+ formData = {
resto: threadID,
name: post.name,
email: post.email,
@@ -8625,6 +8613,7 @@
upfile: post.file,
filetag: filetag,
spoiler: post.spoiler,
+ flag: post.flag,
textonly: textOnly,
mode: 'regist',
pwd: QR.persona.pwd,
@@ -8646,7 +8635,7 @@
}
};
extra = {
- form: $.formData(postData),
+ form: $.formData(formData),
upCallbacks: {
onload: function() {
QR.req.isUploadFinished = true;
@@ -8730,8 +8719,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 + ".",
@@ -8756,7 +8745,8 @@
QR.cooldown.set({
req: req,
post: post,
- isReply: isReply
+ isReply: isReply,
+ threadID: threadID
});
URL = !isReply ? "/" + g.BOARD + "/res/" + threadID : g.VIEW === 'index' && !QR.cooldown.auto && Conf['Open Post in New Tab'] ? "/" + g.BOARD + "/res/" + threadID + "#p" + postID : void 0;
if (URL) {
@@ -9787,7 +9777,7 @@
open: function(post) {
var node;
- if (post.isDead || post.board.ID === 'q') {
+ if (post.isDead) {
return false;
}
DeleteLink.post = post;
@@ -9862,7 +9852,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);
},
@@ -10158,7 +10148,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: "[]\u00A0",
@@ -10178,6 +10167,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 = [];
@@ -10195,7 +10185,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
@@ -10225,7 +10215,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']
}));
@@ -10243,19 +10232,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;
@@ -10285,9 +10273,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() {
@@ -10296,17 +10282,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;
}
@@ -10317,9 +10296,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();
@@ -10349,9 +10328,8 @@
ThreadUpdater.set('status', text, klass);
}
if (ThreadUpdater.postID) {
- ThreadUpdater.cb.checkpost();
+ return ThreadUpdater.cb.checkpost();
}
- return delete ThreadUpdater.req;
}
},
setInterval: function() {
@@ -10364,8 +10342,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;
@@ -10387,20 +10364,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 {
@@ -10574,27 +10560,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
@@ -11375,6 +11340,7 @@
'4plebs': {
domain: 'archive.4plebs.org',
http: true,
+ https: true,
software: 'foolfuuka',
boards: ['hr', 'tg', 'tv', 'x'],
files: ['hr', 'tg', 'tv', 'x']
@@ -11382,10 +11348,10 @@
'fap archive': {
domain: 'fuuka.worldathleticproject.org',
http: true,
- https: false,
+ https: true,
software: 'foolfuuka',
boards: ['b', 'e', 'h', 'hc', 'p', 's', 'u'],
- files: ['b', 'e', 'h', 'hc', 'p', 's', 'u']
+ files: ['b', 'e', 'h', 'hc', 'p', 's', 'soc', 'sp', 'u']
},
'Foolz': {
domain: 'archive.foolz.us',
@@ -11409,8 +11375,8 @@
https: true,
withCredentials: true,
software: 'foolfuuka',
- boards: ['a', 'co', 'gd', 'h', 'jp', 'm', 'mlp', 'q', 'sp', 'tg', 'tv', 'u', 'v', 'vg', 'vp', 'vr', 'wsg'],
- files: ['a', 'gd', 'h', 'jp', 'm', 'q', 'tg', 'u', 'vg', 'vp', 'vr', 'wsg']
+ 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']
},
'Heinessen': {
domain: 'archive.heinessen.com',
@@ -11500,14 +11466,10 @@
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
},
post: function(archive, _arg) {
- var URL, boardID, postID, protocol;
+ var URL, boardID, postID;
boardID = _arg.boardID, postID = _arg.postID;
- protocol = Redirect.protocol(archive);
- if (['Foolz', 'NSFW Foolz'].contains(archive.name)) {
- protocol = 'https://';
- }
- URL = new String("" + protocol + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID);
+ URL = new String("" + (Redirect.protocol(archive)) + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID);
URL.archive = archive;
return URL;
},
@@ -13418,12 +13380,6 @@
}
quote.href = "/" + post.board + "/res/" + href;
}
- Build.capcodeReplies({
- boardID: post.board.ID,
- threadID: post.thread.ID,
- bq: clone,
- capcodeReplies: postObj.capcode_replies
- });
post.nodes.shortComment = comment;
$.replace(comment, clone);
post.nodes.comment = post.nodes.longComment = clone;
@@ -13509,7 +13465,6 @@
switch (g.BOARD.ID) {
case 'b':
case 'vg':
- case 'q':
return 3;
case 't':
return 1;
diff --git a/builds/crx/script.js b/builds/crx/script.js
index 7b2f03c31..84c52ace7 100644
--- a/builds/crx/script.js
+++ b/builds/crx/script.js
@@ -1,6 +1,6 @@
// Generated by CoffeeScript
/*
-* appchan x - Version 2.3.10 - 2013-09-20
+* appchan x - Version 2.3.10 - 2013-09-24
*
* Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
@@ -317,7 +317,7 @@
uniqueID: "# Filter a specific ID:\n#/Txhvk1Tl/",
tripcode: "# Filter any tripfag\n#/^!/",
capcode: "# Set a custom class for mods:\n#/Mod$/;highlight:mod;op:yes\n# Set a custom class for moot:\n#/Admin$/;highlight:moot;op:yes",
- email: "# Filter any e-mails that are not `sage` on /a/ and /jp/:\n#/^(?!sage$)/;boards:a,jp",
+ email: "",
subject: "# Filter Generals on /v/:\n#/general/i;boards:v;op:only",
comment: "# Filter Stallman copypasta on /g/:\n#/what you\'re refer+ing to as linux/i;boards:g",
flag: '',
@@ -3332,7 +3332,7 @@
this.nodes.comment.normalize();
bq = this.nodes.comment.cloneNode(true);
- nodes = $$('.abbr, .capcodeReplies, .exif, b', bq);
+ nodes = $$('.abbr, .exif, b', bq);
i = 0;
while (node = nodes[i++]) {
$.rm(node);
@@ -3364,7 +3364,7 @@
return;
}
this.nodes.quotelinks.push(quotelink);
- if (this.isClone || !this.isReply && $.hasClass(quotelink.parentNode.parentNode, 'capcodeReplies')) {
+ if (this.isClone) {
return;
}
fullID = "" + match[1] + "." + match[2];
@@ -4241,7 +4241,6 @@
date: data.now,
dateUTC: data.time,
comment: data.com,
- capcodeReplies: data.capcode_replies,
isSticky: !!data.sticky,
isClosed: !!data.closed
};
@@ -4269,9 +4268,9 @@
@license: https://github.com/4chan/4chan-JS/blob/master/LICENSE
*/
- var a, boardID, capcode, capcodeClass, capcodeReplies, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref;
+ var a, boardID, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref;
- postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, capcodeReplies = o.capcodeReplies, file = o.file;
+ postID = o.postID, threadID = o.threadID, boardID = o.boardID, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file;
isOP = postID === threadID;
staticPath = '//static.4chan.org/image/';
if (email) {
@@ -4348,7 +4347,7 @@
container = $.el('div', {
id: "pc" + postID,
className: "postContainer " + (isOP ? 'op' : 'reply') + "Container",
- innerHTML: "" + (isOP ? '' : ">>
") + "" + (name || '') + "" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "
" + subject + "" + date + "No." + postID + " " + (isOP ? fileHTML : '') + "
" + subject + "
" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "" + " " + "
" + date + "" + " " + "
No." + postID + "" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + " " + "
"
+ innerHTML: "" + (isOP ? '' : ">>
") + "" + (name || '') + "" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "
" + subject + "" + date + "No." + postID + " " + (isOP ? fileHTML : '') + "
" + subject + "
" + emailStart + "" + (name || '') + "" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "" + " " + "
" + date + "" + " " + "
No." + postID + "" + (isOP ? '' : fileHTML) + "
" + (comment || '') + "
" + " " + "
"
});
_ref = $$('.quotelink', container);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -4359,47 +4358,7 @@
}
quote.href = "/" + boardID + "/res/" + href;
}
- Build.capcodeReplies({
- boardID: boardID,
- threadID: threadID,
- root: container,
- capcodeReplies: capcodeReplies
- });
return container;
- },
- capcodeReplies: function(_arg) {
- var array, boardID, bq, capcodeReplies, capcodeType, generateCapcodeReplies, html, root, threadID;
-
- boardID = _arg.boardID, threadID = _arg.threadID, bq = _arg.bq, root = _arg.root, capcodeReplies = _arg.capcodeReplies;
- if (!capcodeReplies) {
- return;
- }
- generateCapcodeReplies = function(capcodeType, array) {
- return "" + ((function() {
- switch (capcodeType) {
- case 'admin':
- return 'Administrator';
- case 'mod':
- return 'Moderator';
- case 'developer':
- return 'Developer';
- }
- })()) + " Repl" + (array.length > 1 ? 'ies' : 'y') + ": " + (array.map(function(ID) {
- return ">>" + ID + "";
- }).join(' ')) + "
";
- };
- html = [];
- for (capcodeType in capcodeReplies) {
- array = capcodeReplies[capcodeType];
- html.push(generateCapcodeReplies(capcodeType, array));
- }
- bq || (bq = $('blockquote', root));
- return $.add(bq, [
- $.el('br'), $.el('br'), $.el('span', {
- className: 'capcodeReplies',
- innerHTML: html.join('')
- })
- ]);
}
};
@@ -5119,12 +5078,8 @@
delete this.nodes.tripcode;
}
if (this.info.email) {
- if (/sage/i.test(this.info.email)) {
- return email.href = 'mailto:sage';
- } else {
- $.replace(email, name);
- return delete this.nodes.email;
- }
+ $.replace(email, name);
+ return delete this.nodes.email;
}
}
};
@@ -5688,7 +5643,7 @@
return;
}
a = PostHiding.makeButton(post, 'show');
- postInfo = Conf['Anonymize'] ? 'Anonymous' : post.info.name;
+ postInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', post.nodes.info).textContent;
$.add(a, $.tn(" " + postInfo));
post.nodes.stub = $.el('div', {
className: 'stub'
@@ -6036,7 +5991,7 @@
}
numReplies = ((span = $('.summary', threadRoot)) ? +span.textContent.match(/\d+/) : 0) + $$('.opContainer ~ .replyContainer', threadRoot).length;
numReplies = numReplies === 1 ? '1 reply' : "" + (numReplies || 'no') + " replies";
- opInfo = Conf['Anonymize'] ? 'Anonymous' : OP.info.name;
+ opInfo = Conf['Anonymize'] ? 'Anonymous' : $('.nameBlock', OP.nodes.info).textContent;
a = ThreadHiding.makeButton(thread, 'show');
$.add(a, $.tn(" " + opInfo + " (" + numReplies + ")"));
thread.stub = $.el('div', {
@@ -7596,7 +7551,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);
});
@@ -7604,36 +7560,30 @@
},
cooldown: {
init: function() {
- var board;
+ var key, setTimers, type, _base,
+ _this = this;
if (!Conf['Cooldown']) {
return;
}
- board = g.BOARD.ID;
- QR.cooldown.types = {
- thread: (function() {
- switch (board) {
- case 'q':
- return 86400;
- case 'b':
- case 'soc':
- case 'r9k':
- return 600;
- default:
- return 300;
- }
- })(),
- sage: board === 'q' ? 600 : 60,
- file: board === 'q' ? 300 : 30,
- post: board === 'q' ? 150 : 30
+ setTimers = function(e) {
+ return QR.cooldown.types = e.detail;
};
+ $.on(window, 'cooldown:timers', setTimers);
+ $.globalEval('window.dispatchEvent(new CustomEvent("cooldown:timers", {detail: cooldowns}))');
+ (_base = QR.cooldown).types || (_base.types = {});
+ $.off(window, 'cooldown:timers', setTimers);
+ for (type in QR.cooldown.types) {
+ QR.cooldown.types[type] = +QR.cooldown.types[type];
+ }
QR.cooldown.upSpd = 0;
QR.cooldown.upSpdAccuracy = .5;
- $.get("cooldown." + board, {}, function(item) {
- QR.cooldown.cooldowns = item["cooldown." + board];
+ key = "cooldown." + g.BOARD;
+ $.get(key, {}, function(item) {
+ QR.cooldown.cooldowns = item[key];
return QR.cooldown.start();
});
- return $.sync("cooldown." + board, QR.cooldown.sync);
+ return $.sync(key, QR.cooldown.sync);
},
start: function() {
if (!Conf['Cooldown']) {
@@ -7654,31 +7604,27 @@
return QR.cooldown.start();
},
set: function(data) {
- var cooldown, delay, hasFile, isReply, isSage, post, req, start, type, upSpd;
+ var cooldown, delay, hasFile, isReply, post, req, start, threadID, upSpd;
if (!Conf['Cooldown']) {
return;
}
- req = data.req, post = data.post, isReply = data.isReply, delay = data.delay;
+ req = data.req, post = data.post, isReply = data.isReply, threadID = data.threadID, delay = data.delay;
start = req ? req.uploadEndTime : Date.now();
if (delay) {
cooldown = {
delay: delay
};
} else {
- if (post.file) {
- upSpd = post.file.size / ((req.uploadEndTime - req.uploadStartTime) / $.SECOND);
+ 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;
}
- isSage = /sage/i.test(post.email);
- hasFile = !!post.file;
- type = !isReply ? 'thread' : isSage ? 'sage' : hasFile ? 'file' : 'post';
cooldown = {
isReply: isReply,
- isSage: isSage,
hasFile: hasFile,
- timeout: start + QR.cooldown.types[type] * $.SECOND
+ threadID: threadID
};
}
QR.cooldown.cooldowns[start] = cooldown;
@@ -7694,7 +7640,7 @@
}
},
count: function() {
- var cooldown, cooldowns, elapsed, hasFile, isReply, isSage, now, post, seconds, start, type, types, upSpd, upSpdAccuracy, update, _ref;
+ var cooldown, cooldowns, elapsed, hasFile, isReply, maxTimer, now, post, seconds, start, type, types, upSpd, upSpdAccuracy, update, _ref;
if (!Object.keys(QR.cooldown.cooldowns).length) {
$["delete"]("" + g.BOARD + ".cooldown");
@@ -7703,11 +7649,11 @@
QR.status();
return;
}
- setTimeout(QR.cooldown.count, $.SECOND);
+ clearTimeout(QR.cooldown.timeout);
+ QR.cooldown.timeout = setTimeout(QR.cooldown.count, $.SECOND);
now = Date.now();
post = QR.posts[0];
isReply = post.thread !== 'new';
- isSage = /sage/i.test(post.email);
hasFile = !!post.file;
seconds = null;
_ref = QR.cooldown, types = _ref.types, cooldowns = _ref.cooldowns, upSpd = _ref.upSpd, upSpdAccuracy = _ref.upSpdAccuracy;
@@ -7722,20 +7668,39 @@
}
continue;
}
- if (isReply === cooldown.isReply) {
- type = !isReply ? 'thread' : isSage && cooldown.isSage ? 'sage' : hasFile && cooldown.hasFile ? 'file' : 'post';
- elapsed = Math.floor((now - start) / $.SECOND);
- if (elapsed >= 0) {
- seconds = Math.max(seconds, types[type] - elapsed);
- if (Conf['Cooldown Prediction'] && hasFile && upSpd) {
- seconds -= Math.floor(post.file.size / upSpd * upSpdAccuracy);
- seconds = Math.max(seconds, 0);
- }
- }
- }
- if (!((start <= now && now <= cooldown.timeout))) {
+ if ('timeout' in cooldown) {
QR.cooldown.unset(start);
+ continue;
}
+ if (isReply === cooldown.isReply) {
+ elapsed = Math.floor((now - start) / $.SECOND);
+ if (elapsed < 0) {
+ continue;
+ }
+ 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);
+ }
+ if (isReply && +post.thread === cooldown.threadID) {
+ type += '_intra';
+ }
+ seconds = Math.max(seconds, types[type] - elapsed);
+ }
+ }
+ if (seconds && Conf['Cooldown Prediction'] && hasFile && upSpd) {
+ seconds -= Math.floor(post.file.size / upSpd * upSpdAccuracy);
+ seconds = seconds > 0 ? seconds : 0;
}
update = seconds !== null || !!QR.cooldown.seconds;
QR.cooldown.seconds = seconds;
@@ -7948,6 +7913,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();
}
@@ -7979,7 +7947,7 @@
};
_Class.prototype.lock = function(lock) {
- var name, _i, _len, _ref;
+ var name, node, _i, _len, _ref;
if (lock == null) {
lock = true;
@@ -7988,10 +7956,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);
@@ -8021,12 +7991,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;
}
QR.tripcodeHider.call(QR.nodes['name']);
this.showFileData();
@@ -8041,7 +8014,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();
@@ -8065,15 +8038,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);
}
};
@@ -8409,7 +8385,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;', "")
@@ -8469,8 +8445,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,
@@ -8507,12 +8490,17 @@
while (name = items[i++]) {
$.on(nodes[name], 'mouseover', QR.mouseover);
}
- items = ['name', 'email', 'sub', 'com', 'filename'];
+ items = ['name', 'email', 'sub', 'com', 'filename', 'flag'];
i = 0;
+ save = function() {
+ return QR.selected.save(this);
+ };
while (name = items[i++]) {
- $.on(nodes[name], 'input', function() {
- return QR.selected.save(this);
- });
+ if (!(node = nodes[name])) {
+ continue;
+ }
+ event = node.nodeName === 'SELECT' ? 'change' : 'input';
+ $.on(nodes[name], event, save);
}
$.on(nodes['name'], 'blur', QR.tripcodeHider);
$.on(nodes.thread, 'change', function() {
@@ -8542,7 +8530,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();
@@ -8565,7 +8553,7 @@
thread = g.BOARD.threads[threadID];
if (threadID === 'new') {
threadID = null;
- if (['vg', 'q'].contains(g.BOARD.ID) && !post.sub) {
+ if (g.BOARD.ID === 'vg' && !post.sub) {
err = 'New threads require a subject.';
} else if (!(post.file || (textOnly = !!$('input[name=textonly]', $.id('postForm'))))) {
err = 'No file selected.';
@@ -8606,7 +8594,7 @@
d.activeElement.blur();
}
post.lock();
- postData = {
+ formData = {
resto: threadID,
name: post.name,
email: post.email,
@@ -8615,6 +8603,7 @@
upfile: post.file,
filetag: filetag,
spoiler: post.spoiler,
+ flag: post.flag,
textonly: textOnly,
mode: 'regist',
pwd: QR.persona.pwd,
@@ -8636,7 +8625,7 @@
}
};
extra = {
- form: $.formData(postData),
+ form: $.formData(formData),
upCallbacks: {
onload: function() {
QR.req.isUploadFinished = true;
@@ -8720,8 +8709,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 + ".",
@@ -8746,7 +8735,8 @@
QR.cooldown.set({
req: req,
post: post,
- isReply: isReply
+ isReply: isReply,
+ threadID: threadID
});
URL = !isReply ? "/" + g.BOARD + "/res/" + threadID : g.VIEW === 'index' && !QR.cooldown.auto && Conf['Open Post in New Tab'] ? "/" + g.BOARD + "/res/" + threadID + "#p" + postID : void 0;
if (URL) {
@@ -9777,7 +9767,7 @@
open: function(post) {
var node;
- if (post.isDead || post.board.ID === 'q') {
+ if (post.isDead) {
return false;
}
DeleteLink.post = post;
@@ -9852,7 +9842,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);
},
@@ -10148,7 +10138,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: "[]\u00A0",
@@ -10168,6 +10157,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 = [];
@@ -10185,7 +10175,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
@@ -10215,7 +10205,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']
}));
@@ -10233,19 +10222,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;
@@ -10275,9 +10263,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() {
@@ -10286,17 +10272,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;
}
@@ -10307,9 +10286,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();
@@ -10339,9 +10318,8 @@
ThreadUpdater.set('status', text, klass);
}
if (ThreadUpdater.postID) {
- ThreadUpdater.cb.checkpost();
+ return ThreadUpdater.cb.checkpost();
}
- return delete ThreadUpdater.req;
}
},
setInterval: function() {
@@ -10354,8 +10332,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;
@@ -10377,20 +10354,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 {
@@ -10564,27 +10550,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
@@ -11371,6 +11336,7 @@
'4plebs': {
domain: 'archive.4plebs.org',
http: true,
+ https: true,
software: 'foolfuuka',
boards: ['hr', 'tg', 'tv', 'x'],
files: ['hr', 'tg', 'tv', 'x']
@@ -11378,10 +11344,10 @@
'fap archive': {
domain: 'fuuka.worldathleticproject.org',
http: true,
- https: false,
+ https: true,
software: 'foolfuuka',
boards: ['b', 'e', 'h', 'hc', 'p', 's', 'u'],
- files: ['b', 'e', 'h', 'hc', 'p', 's', 'u']
+ files: ['b', 'e', 'h', 'hc', 'p', 's', 'soc', 'sp', 'u']
},
'Foolz': {
domain: 'archive.foolz.us',
@@ -11405,8 +11371,8 @@
https: true,
withCredentials: true,
software: 'foolfuuka',
- boards: ['a', 'co', 'gd', 'h', 'jp', 'm', 'mlp', 'q', 'sp', 'tg', 'tv', 'u', 'v', 'vg', 'vp', 'vr', 'wsg'],
- files: ['a', 'gd', 'h', 'jp', 'm', 'q', 'tg', 'u', 'vg', 'vp', 'vr', 'wsg']
+ 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']
},
'Heinessen': {
domain: 'archive.heinessen.com',
@@ -11496,14 +11462,10 @@
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
},
post: function(archive, _arg) {
- var URL, boardID, postID, protocol;
+ var URL, boardID, postID;
boardID = _arg.boardID, postID = _arg.postID;
- protocol = Redirect.protocol(archive);
- if (['Foolz', 'NSFW Foolz'].contains(archive.name)) {
- protocol = 'https://';
- }
- URL = new String("" + protocol + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID);
+ URL = new String("" + (Redirect.protocol(archive)) + archive.domain + "/_/api/chan/post/?board=" + boardID + "&num=" + postID);
URL.archive = archive;
return URL;
},
@@ -13414,12 +13376,6 @@
}
quote.href = "/" + post.board + "/res/" + href;
}
- Build.capcodeReplies({
- boardID: post.board.ID,
- threadID: post.thread.ID,
- bq: clone,
- capcodeReplies: postObj.capcode_replies
- });
post.nodes.shortComment = comment;
$.replace(comment, clone);
post.nodes.comment = post.nodes.longComment = clone;
@@ -13505,7 +13461,6 @@
switch (g.BOARD.ID) {
case 'b':
case 'vg':
- case 'q':
return 3;
case 't':
return 1;
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 beb9c4786..7edd56218 100644
--- a/package.json
+++ b/package.json
@@ -22,14 +22,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/Archive/Redirect.coffee b/src/Archive/Redirect.coffee
index 9eaa101c0..c34f48910 100755
--- a/src/Archive/Redirect.coffee
+++ b/src/Archive/Redirect.coffee
@@ -24,7 +24,8 @@ Redirect =
archives:
'4plebs':
domain: 'archive.4plebs.org'
- http: true
+ http: true
+ https: true
software: 'foolfuuka'
boards: ['hr', 'tg', 'tv', 'x']
files: ['hr', 'tg', 'tv', 'x']
@@ -32,10 +33,10 @@ Redirect =
'fap archive':
domain: 'fuuka.worldathleticproject.org'
http: true
- https: false
+ https: true
software: 'foolfuuka'
boards: ['b', 'e', 'h', 'hc', 'p', 's', 'u']
- files: ['b', 'e', 'h', 'hc', 'p', 's', 'u']
+ files: ['b', 'e', 'h', 'hc', 'p', 's', 'soc', 'sp', 'u']
'Foolz':
domain: 'archive.foolz.us'
@@ -59,8 +60,8 @@ Redirect =
https: true
withCredentials: true
software: 'foolfuuka'
- boards: ['a', 'co', 'gd', 'h', 'jp', 'm', 'mlp', 'q', 'sp', 'tg', 'tv', 'u', 'v', 'vg', 'vp', 'vr', 'wsg'],
- files: ['a', 'gd', 'h', 'jp', 'm', 'q', 'tg', 'u', 'vg', 'vp', 'vr', 'wsg']
+ 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']
'Heinessen':
domain: 'archive.heinessen.com'
@@ -146,12 +147,7 @@ Redirect =
post: (archive, {boardID, postID}) ->
# For fuuka-based archives:
# https://github.com/eksopl/fuuka/issues/27
- protocol = Redirect.protocol archive
- # XXX foolz had HSTS set for 120 days, which broke XHR+CORS+Redirection when on HTTP.
- # Remove necessary HTTPS procotol in September 2013.
- if ['Foolz', 'NSFW Foolz'].contains archive.name
- protocol = 'https://'
- URL = new String "#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}"
+ URL = new String "#{Redirect.protocol archive}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}"
URL.archive = archive
URL
diff --git a/src/Filtering/Anonymize.coffee b/src/Filtering/Anonymize.coffee
index 40e991564..d5b80f99c 100755
--- a/src/Filtering/Anonymize.coffee
+++ b/src/Filtering/Anonymize.coffee
@@ -14,8 +14,5 @@ Anonymize =
$.rm tripcode
delete @nodes.tripcode
if @info.email
- if /sage/i.test @info.email
- email.href = 'mailto:sage'
- else
- $.replace email, name
- delete @nodes.email
\ No newline at end of file
+ $.replace email, name
+ delete @nodes.email
diff --git a/src/Filtering/PostHiding.coffee b/src/Filtering/PostHiding.coffee
index 73c1055f7..afc998665 100644
--- a/src/Filtering/PostHiding.coffee
+++ b/src/Filtering/PostHiding.coffee
@@ -184,7 +184,7 @@ PostHiding =
if Conf['Anonymize']
'Anonymous'
else
- post.info.name
+ $('.nameBlock', post.nodes.info).textContent
$.add a, $.tn " #{postInfo}"
post.nodes.stub = $.el 'div',
className: 'stub'
diff --git a/src/Filtering/ThreadHiding.coffee b/src/Filtering/ThreadHiding.coffee
index fb85d27ea..1ce165cfd 100644
--- a/src/Filtering/ThreadHiding.coffee
+++ b/src/Filtering/ThreadHiding.coffee
@@ -185,7 +185,7 @@ ThreadHiding =
if Conf['Anonymize']
'Anonymous'
else
- OP.info.name
+ $('.nameBlock', OP.nodes.info).textContent
a = ThreadHiding.makeButton thread, 'show'
$.add a, $.tn " #{opInfo} (#{numReplies})"
diff --git a/src/General/Build.coffee b/src/General/Build.coffee
index 6a1683a38..b56123ae2 100755
--- a/src/General/Build.coffee
+++ b/src/General/Build.coffee
@@ -27,7 +27,6 @@ Build =
date: data.now
dateUTC: data.time
comment: data.com
- capcodeReplies: data.capcode_replies
# thread status
isSticky: !!data.sticky
isClosed: !!data.closed
@@ -59,7 +58,7 @@ Build =
postID, threadID, boardID
name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC
isSticky, isClosed
- comment, capcodeReplies
+ comment
file
} = o
isOP = postID is threadID
@@ -201,36 +200,4 @@ Build =
continue if href[0] is '/' # Cross-board quote, or board link
quote.href = "/#{boardID}/res/#{href}" # Fix pathnames
- Build.capcodeReplies {boardID, threadID, root: container, capcodeReplies}
-
container
-
- capcodeReplies: ({boardID, threadID, bq, root, capcodeReplies}) ->
- return unless capcodeReplies
-
- generateCapcodeReplies = (capcodeType, array) ->
- "#{
- switch capcodeType
- when 'admin'
- 'Administrator'
- when 'mod'
- 'Moderator'
- when 'developer'
- 'Developer'
- } Repl#{if array.length > 1 then 'ies' else 'y'}: #{
- array.map (ID) ->
- ">>#{ID}"
- .join ' '
- }
"
- html = []
- for capcodeType, array of capcodeReplies
- html.push generateCapcodeReplies capcodeType, array
-
- bq or= $ 'blockquote', root
- $.add bq, [
- $.el 'br'
- $.el 'br'
- $.el 'span',
- className: 'capcodeReplies'
- innerHTML: html.join ''
- ]
diff --git a/src/General/Config.coffee b/src/General/Config.coffee
index 9e7af4156..5fed5c08d 100755
--- a/src/General/Config.coffee
+++ b/src/General/Config.coffee
@@ -799,12 +799,8 @@ Config =
#/Mod$/;highlight:mod;op:yes
# Set a custom class for moot:
#/Admin$/;highlight:moot;op:yes
-"""
-
- email: """
-# Filter any e-mails that are not `sage` on /a/ and /jp/:
-#/^(?!sage$)/;boards:a,jp
-"""
+ """
+ email: ""
subject: """
# Filter Generals on /v/:
#/general/i;boards:v;op:only
diff --git a/src/General/css/style.css b/src/General/css/style.css
new file mode 100755
index 000000000..876fc366c
--- /dev/null
+++ b/src/General/css/style.css
@@ -0,0 +1,1271 @@
+/* General */
+.dialog {
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .15);
+ border: 1px solid;
+ display: block;
+ padding: 0;
+}
+.captcha-img,
+.field {
+ background-color: #FFF;
+ border: 1px solid #CCC;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ color: #333;
+ font: 13px sans-serif;
+ outline: none;
+ transition: color .25s, border-color .25s;
+ transition: color .25s, border-color .25s;
+}
+.field::-moz-placeholder,
+.field:hover::-moz-placeholder {
+ color: #AAA !important;
+ font-size: 13px !important;
+ opacity: 1.0 !important;
+}
+.captch-img:hover,
+.field:hover {
+ border-color: #999;
+}
+.field:hover, .field:focus {
+ color: #000;
+}
+.field[disabled] {
+ background-color: #F2F2F2;
+ color: #888;
+}
+.move {
+ cursor: move;
+ overflow: hidden;
+}
+label,
+.watch-thread-link {
+ cursor: pointer;
+}
+a[href="javascript:;"] {
+ text-decoration: none;
+}
+.warning {
+ color: red;
+}
+#boardNavDesktop {
+ display: none !important;
+}
+a {
+ outline: none !important;
+}
+.painted {
+ border-radius: 3px;
+ padding: 0px 2px;
+}
+
+/* 4chan style fixes */
+.opContainer, .op {
+ display: block !important;
+ overflow: visible !important;
+}
+[hidden] {
+ display: none !important;
+}
+
+/* fixed, z-index */
+#overlay,
+#fourchanx-settings,
+#qp, #ihover,
+#navlinks, .fixed #header-bar,
+:root.float #updater,
+:root.float #thread-stats,
+#qr {
+ position: fixed;
+}
+#fourchanx-settings {
+ z-index: 999;
+}
+#overlay {
+ z-index: 900;
+}
+#notifications {
+ z-index: 70;
+}
+#qp, #ihover {
+ z-index: 60;
+}
+#menu {
+ z-index: 50;
+}
+#navlinks, #updater, #thread-stats {
+ z-index: 40;
+}
+.fixed #header-bar.autohide {
+ z-index: 35;
+}
+#qr {
+ z-index: 30;
+}
+#thread-watcher {
+ z-index: 8;
+}
+:root.fixed-watcher #thread-watcher {
+ z-index: 20;
+}
+.fixed #header-bar {
+ z-index: 10;
+}
+/* Header */
+.fixed.top body {
+ padding-top: 2em;
+}
+.fixed.bottom body {
+ padding-bottom: 2em;
+}
+.fixed #header-bar {
+ right: 0;
+ left: 0;
+ padding: 3px 4px 4px;
+}
+.fixed.top #header-bar {
+ top: 0;
+}
+.fixed.bottom #header-bar {
+ bottom: 0;
+}
+#header-bar {
+ border-width: 0;
+ transition: all .1s .05s ease-in-out;
+}
+:root.centered-links #shortcuts {
+ width: 300px;
+ text-align: right;
+}
+:root.centered-links #header-bar {
+ text-align: center;
+}
+:root.centered-links #custom-board-list {
+ position: relative;
+ left: 150px;
+}
+.fixed.top #header-bar {
+ border-bottom-width: 1px;
+}
+.fixed.bottom #header-bar {
+ box-shadow: 0 -1px 2px rgba(0, 0, 0, .15);
+ border-top-width: 1px;
+}
+.fixed.bottom #header-bar .menu-button i {
+ border-top: none;
+ border-bottom: 6px solid;
+}
+#board-list {
+ text-align: center;
+}
+.fixed #header-bar.autohide:not(:hover) {
+ box-shadow: none;
+ transition: all .8s .6s cubic-bezier(.55, .055, .675, .19);
+}
+.fixed.top #header-bar.autohide:not(:hover) {
+ margin-bottom: -1em;
+ -webkit-transform: translateY(-100%);
+ transform: translateY(-100%);
+}
+.fixed.bottom #header-bar.autohide:not(:hover) {
+ -webkit-transform: translateY(100%);
+ transform: translateY(100%);
+}
+#scroll-marker {
+ left: 0;
+ right: 0;
+ height: 10px;
+ position: absolute;
+}
+:root:not(.autohide) #scroll-marker {
+ pointer-events: none;
+}
+#header-bar #scroll-marker {
+ display: none;
+}
+.fixed #header-bar #scroll-marker {
+ display: block;
+}
+.fixed.top #header-bar #scroll-marker {
+ top: 100%;
+}
+.fixed.bottom #header-bar #scroll-marker {
+ bottom: 100%;
+}
+#header-bar a:not(.entry):not(.close) {
+ text-decoration: none;
+ padding: 1px;
+}
+#header-bar input {
+ margin: 0;
+ vertical-align: bottom;
+}
+#shortcuts:empty {
+ display: none;
+}
+.brackets-wrap::before {
+ content: "\\00a0[";
+}
+.brackets-wrap::after {
+ content: "]\\00a0";
+}
+.dead-thread,
+.disabled {
+ opacity: .45;
+}
+#shortcuts {
+ float: right;
+}
+.shortcut {
+ margin-left: 3px;
+}
+#navbotright,
+#navtopright {
+ display: none;
+}
+#toggleMsgBtn {
+ display: none !important;
+}
+.current {
+ font-weight: bold;
+}
+/* 4chan X link brackets */
+.brackets-wrap::after {
+ content: "]";
+}
+.brackets-wrap::before {
+ content: "[";
+}
+/* Notifications */
+#notifications {
+ position: fixed;
+ top: 0;
+ height: 0;
+ text-align: center;
+ right: 0;
+ left: 0;
+ transition: all .8s .6s cubic-bezier(.55, .055, .675, .19);
+}
+.fixed.top #header-bar #notifications {
+ position: absolute;
+ top: 100%;
+}
+.notification {
+ color: #FFF;
+ font-weight: 700;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, .5);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .15);
+ border-radius: 2px;
+ margin: 1px auto;
+ width: 500px;
+ max-width: 100%;
+ position: relative;
+ transition: all .25s ease-in-out;
+}
+.notification.error {
+ background-color: hsla(0, 100%, 38%, .9);
+}
+.notification.warning {
+ background-color: hsla(36, 100%, 38%, .9);
+}
+.notification.info {
+ background-color: hsla(200, 100%, 38%, .9);
+}
+.notification.success {
+ background-color: hsla(104, 100%, 38%, .9);
+}
+.notification a {
+ color: white;
+}
+.notification > .close {
+ padding: 6px;
+ top: 0;
+ right: 5px;
+ position: absolute;
+}
+.message {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 6px 20px;
+ max-height: 200px;
+ width: 100%;
+ overflow: auto;
+}
+
+/* Settings */
+:root.fourchan-x body {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+#overlay {
+ background-color: rgba(0, 0, 0, .5);
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+}
+#fourchanx-settings {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ box-shadow: 0 0 15px rgba(0, 0, 0, .15);
+ height: 600px;
+ max-height: 100%;
+ width: 900px;
+ max-width: 100%;
+ margin: auto;
+ padding: 3px;
+ top: 50%;
+ left: 50%;
+ -moz-transform: translate(-50%, -50%);
+ -webkit-transform: translate(-50%, -50%);
+ transform: translate(-50%, -50%);
+}
+#fourchanx-settings > nav {
+ padding: 2px 2px 0;
+ height: 15px;
+}
+#fourchanx-settings > nav a {
+ text-decoration: underline;
+}
+#fourchanx-settings > nav a.close {
+ text-decoration: none;
+ padding: 2px;
+}
+.section-container {
+ overflow: auto;
+ position: absolute;
+ top: 2.1em;
+ right: 5px;
+ bottom: 5px;
+ left: 5px;
+ padding-right: 5px;
+}
+.sections-list {
+ padding: 0 3px;
+ float: left;
+}
+.credits {
+ float: right;
+}
+.tab-selected {
+ font-weight: 700;
+}
+.section-sauce ul,
+.section-advanced ul {
+ list-style: none;
+ margin: 0;
+}
+.section-sauce ul {
+ padding: 8px;
+}
+.section-advanced ul {
+ padding: 0px;
+}
+.section-sauce li,
+.section-advanced li {
+ padding-left: 4px;
+}
+.section-main label {
+ text-decoration: underline;
+}
+.section-filter ul {
+ padding: 0;
+}
+.section-filter li {
+ margin: 10px 40px;
+}
+.section-filter textarea {
+ height: 500px;
+}
+.section-sauce textarea {
+ height: 350px;
+}
+.section-advanced .field[name="boardnav"] {
+ width: 100%;
+}
+.section-advanced textarea {
+ height: 150px;
+}
+.section-advanced .archive-cell {
+ min-width: 160px;
+ text-align: center;
+}
+.section-advanced #archive-board-select {
+ position: absolute;
+}
+.section-advanced .note {
+ font-size: 0.8em;
+ font-style: italic;
+ margin-left: 10px;
+}
+.section-advanced .note code {
+ font-style: normal;
+ font-size: 11px;
+}
+.section-keybinds .field {
+ font-family: monospace;
+}
+#fourchanx-settings fieldset {
+ border: 1px solid;
+ border-radius: 3px;
+}
+#fourchanx-settings legend {
+ font-weight: 700;
+}
+#fourchanx-settings textarea {
+ font-family: monospace;
+ min-width: 100%;
+ max-width: 100%;
+}
+#fourchanx-settings code {
+ color: #000;
+ background-color: #FFF;
+ padding: 0 2px;
+}
+.unscroll {
+ overflow: hidden;
+}
+
+/* Announcement Hiding */
+:root.hide-announcement #globalMessage {
+ display: none;
+}
+a.hide-announcement {
+ float: left;
+}
+
+/* Unread */
+#unread-line {
+ margin: 0;
+ border-color: rgb(255,0,0);
+}
+
+/* Thread Updater */
+#updater {
+ background: none;
+ border: none;
+ box-shadow: none;
+}
+#updater > .move {
+ padding: 5px 3px 0px;
+ margin-bottom: -3px;
+}
+#updater > div:last-child {
+ text-align: center;
+}
+#updater input[type=number] {
+ width: 4em;
+}
+:root.float #updater {
+ padding: 0px 3px;
+}
+.new {
+ color: limegreen;
+}
+#update-status.new {
+ margin-right: 5px;
+}
+#update-timer {
+ cursor: pointer;
+}
+
+/* Thread Watcher */
+#thread-watcher {
+ position: absolute;
+}
+#thread-watcher {
+ padding-bottom: 3px;
+ padding-left: 3px;
+ overflow: hidden;
+ white-space: nowrap;
+ min-width: 136px;
+ max-height: 92%;
+ overflow-y: auto;
+}
+#thread-watcher .menu-button {
+ bottom: 1px;
+}
+:root.fixed-watcher #thread-watcher {
+ position: fixed;
+}
+:root:not(.fixed-watcher) #thread-watcher:not(:hover) {
+ max-height: 210px;
+ overflow-y: hidden;
+}
+#thread-watcher > .move {
+ padding-top: 3px;
+}
+#watched-threads > div {
+ max-width: 250px;
+ overflow: hidden;
+ padding-left: 3px;
+ padding-right: 3px;
+ text-overflow: ellipsis;
+}
+#thread-watcher a {
+ text-decoration: none;
+}
+#thread-watcher .move>.close {
+ position: absolute;
+ right: 0px;
+ top: 0px;
+ padding: 0px 4px;
+}
+.watch-thread-link {
+ padding-top: 18px;
+ width: 18px;
+ height: 0px;
+ display: inline-block;
+ background-repeat: no-repeat;
+ opacity: 0.2;
+ position: relative;
+ top: 1px;
+}
+.watch-thread-link.watched {
+ opacity: 1;
+}
+
+
+/* Thread Stats */
+#thread-stats {
+ background: none;
+ border: none;
+ box-shadow: none;
+}
+:root.float #post-count, :root.float #file-count {
+ pointer-events: none;
+}
+:root.float #thread-stats {
+ padding: 0px 3px;
+}
+
+/* Quote */
+.deadlink {
+ text-decoration: none !important;
+}
+.backlink.deadlink:not(.forwardlink),
+.quotelink.deadlink:not(.forwardlink) {
+ text-decoration: underline !important;
+}
+.inlined {
+ opacity: .5;
+}
+#qp input, .forwarded {
+ display: none;
+}
+.quotelink.forwardlink,
+.backlink.forwardlink {
+ text-decoration: none;
+ border-bottom: 1px dashed;
+}
+.filtered {
+ text-decoration: underline line-through;
+}
+:root.hide-backlinks .backlink.filtered {
+ display: none;
+}
+.inline {
+ border: 1px solid;
+ display: table;
+ margin: 2px 0;
+}
+.inline .post {
+ border: 0 !important;
+ background-color: transparent !important;
+ display: table !important;
+ margin: 0 !important;
+ padding: 1px 2px !important;
+}
+#qp > .opContainer::after {
+ content: '';
+ clear: both;
+ display: table;
+}
+#qp .post {
+ border: none;
+ margin: 0;
+ padding: 2px 2px 5px;
+}
+#qp img {
+ max-height: 80vh;
+ max-width: 50vw;
+}
+.qphl {
+ outline: 2px solid rgba(216, 94, 49, .7);
+}
+:root.highlight-own .yourPost > .reply,
+:root.highlight-you .quotesYou > .reply {
+ border-left: 2px solid rgba(221,0,0,.5);
+}
+/* Quote Threading */
+.threadContainer {
+ margin-left: 20px;
+ border-left: 1px solid rgba(128,128,128,.3);
+}
+.threadOP {
+ clear: both;
+}
+
+/* File */
+.fileText:hover .fntrunc,
+.fileText:not(:hover) .fnfull,
+.expanded-image > .post > .file > .fileThumb > img[data-md5],
+:not(.expanded-image) > .post > .file > .fileThumb > .full-image {
+ display: none;
+}
+.expanding {
+ opacity: .5;
+}
+:root.fit-height .full-image {
+ max-height: 100vh;
+}
+:root.fit-width .full-image {
+ max-width: 100%;
+}
+:root.gecko.fit-width .full-image {
+ width: 100%;
+}
+#ihover {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ max-height: 100%;
+ max-width: 75%;
+ padding-bottom: 16px;
+}
+.fappeTyme .thread > .noFile,
+.fappeTyme .threadContainer > .noFile {
+ display: none;
+}
+
+/* Index/Reply Navigation */
+#navlinks {
+ font-size: 16px;
+ top: 25px;
+ right: 10px;
+}
+
+/* Filter */
+.opContainer.filter-highlight {
+ box-shadow: inset 5px 0 rgba(255, 0, 0, .5);
+}
+.filter-highlight > .reply {
+ box-shadow: -5px 0 rgba(255, 0, 0, .5);
+}
+
+/* Spoiler text */
+:root.reveal-spoilers s {
+ color: white !important;
+}
+
+/* Thread & Reply Hiding */
+.hide-thread-button,
+.hide-reply-button {
+ float: left;
+ margin-right: 2px;
+}
+.stub ~ * {
+ display: none !important;
+}
+.stub input {
+ display: inline-block;
+}
+
+/* QR */
+:root.hide-original-post-form #postForm,
+:root.hide-original-post-form .postingMode,
+:root.hide-original-post-form #togglePostForm,
+#qr.autohide:not(.has-focus):not(:hover) > form,
+.postingMode ~ #qr select,
+#file-n-submit:not(.has-file) #qr-filerm {
+ display: none;
+}
+#qr select,
+#dump-button,
+.remove,
+.captcha-img {
+ cursor: pointer;
+}
+#qr {
+ z-index: 20;
+ position: fixed;
+ padding: 1px;
+ border: 1px solid transparent;
+ min-width: 300px;
+ border-radius: 3px 3px 0 0;
+}
+#qrtab {
+ border-radius: 3px 3px 0 0;
+}
+#qrtab {
+ margin-bottom: 1px;
+}
+#qr .close {
+ float: right;
+ padding: 0 3px;
+}
+#qr .warning {
+ min-height: 1.6em;
+ vertical-align: middle;
+ padding: 0 1px;
+ border-width: 1px;
+ border-style: solid;
+}
+.qr-link-container {
+ text-align: center;
+}
+.persona {
+ width: 248px;
+ max-width: 100%;
+ min-width: 100%;
+}
+#dump-button {
+ width: 10%;
+ margin: 0;
+ margin-right: 4px;
+ font: 13px sans-serif;
+ padding: 1px 0px 2px;
+ opacity: 0.6;
+}
+.persona .field:not(#dump) {
+ width: 95px;
+ min-width: 33.3%;
+ max-width: 33.3%;
+}
+#qr textarea.field {
+ height: 14.8em;
+ min-height: 9em;
+}
+#qr.has-captcha textarea.field {
+ height: 9em;
+}
+input.field.tripped:not(:hover):not(:focus) {
+ color: transparent !important;
+ text-shadow: none !important;
+}
+#qr textarea {
+ resize: both;
+}
+.captcha-img {
+ margin: 0px;
+ text-align: center;
+ background-image: #fff;
+ font-size: 0px;
+ min-height: 59px;
+ min-width: 302px;
+}
+.captcha-input {
+ width: 100%;
+ margin: 1px 0 0;
+}
+.captcha-input.error:focus {
+ border-color: rgb(255,0,0) !important;
+}
+.field {
+ -moz-box-sizing: border-box;
+ margin: 0px;
+ padding: 2px 4px 3px;
+}
+#qr textarea {
+ min-width: 100%;
+}
+#qr [type='submit'] {
+ width: 25%;
+ vertical-align: top;
+}
+:root.webkit #qr [type='submit'] {
+ height: 24px;
+}
+/* Fake File Input */
+input#qr-filename {
+ border: none !important;
+ width: 80%;
+ padding: 0px 4px;
+ position: relative;
+ bottom: 1px;
+ background: none !important;
+}
+input#qr-filename:not(.edit) {
+ pointer-events: none;
+}
+#qr-filename,
+#qr-filesize,
+.has-file #qr-no-file {
+ display: none;
+}
+#qr-no-file,
+.has-file #qr-filename,
+.has-file #qr-filesize {
+ display: inline-block;
+ margin: 0 0 2px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ vertical-align: top;
+}
+#qr-no-file {
+ color: #AAA;
+ padding: 1px 4px;
+}
+#qr-filename-container {
+ -moz-box-sizing: border-box;
+ display: inline-block;
+ position: relative;
+ width: 100px;
+ min-width: 74.6%;
+ max-width: 74.6%;
+ margin-right: 0.4%;
+ margin-top: 1px;
+ overflow: hidden;
+ padding: 2px 1px 0;
+ height: 22px;
+}
+#qr-filename-container:hover {
+ cursor: text;
+}
+#qr-extras-container {
+ position: absolute;
+ right: 0px;
+}
+#qr-filerm {
+ margin-right: 2px;
+ z-index: 2;
+}
+#file-n-submit {
+ height: 23px;
+}
+#qr input[type=file] {
+ visibility: hidden;
+ position: absolute;
+}
+/* Thread Select / Spoiler Label */
+#qr select {
+ float: right;
+}
+#qr.has-spoiler .has-file #qr-spoiler-label {
+ width: 6.7%;
+ min-width: 6.7%;
+ max-width: 6.7%;
+ display: inline-block;
+ text-align: center;
+ vertical-align: top;
+}
+#qr.has-spoiler #file-n-submit:not(.has-file) #qr-spoiler-label {
+ display: none;
+}
+#qr.has-spoiler .has-file #qr-filename-container {
+ max-width: 67.9%;
+ min-width: 67.9%;
+}
+#qr-spoiler-label input {
+ position: relative;
+ top: 3px;
+}
+/* Dumping UI */
+.dump #dump-list-container {
+ display: block;
+}
+#dump-list-container {
+ display: none;
+ position: relative;
+ overflow-y: hidden;
+ margin-top: 1px;
+}
+#dump-list {
+ overflow-x: auto;
+ overflow-y: hidden;
+ white-space: nowrap;
+ width: 248px;
+ max-width: 100%;
+ min-width: 100%;
+}
+#dump-list:hover {
+ overflow-x: auto;
+}
+.qr-preview {
+ -moz-box-sizing: border-box;
+ counter-increment: thumbnails;
+ cursor: move;
+ display: inline-block;
+ height: 90px;
+ width: 90px;
+ padding: 2px;
+ opacity: .5;
+ overflow: hidden;
+ position: relative;
+ text-shadow: 0 1px 1px #000;
+ -moz-transition: opacity .25s ease-in-out;
+ vertical-align: top;
+ background-size: cover;
+}
+.qr-preview:hover,
+.qr-preview:focus {
+ opacity: .9;
+}
+.qr-preview::before {
+ content: counter(thumbnails);
+ color: #fff;
+ position: absolute;
+ top: 3px;
+ right: 3px;
+ text-shadow: 0 0 3px #000, 0 0 8px #000;
+}
+.qr-preview#selected {
+ opacity: 1;
+}
+.qr-preview.drag {
+ box-shadow: 0 0 10px rgba(0,0,0,.5);
+}
+.qr-preview.over {
+ border-color: #fff;
+}
+.qr-preview > span {
+ color: #fff;
+}
+.remove {
+ background: none;
+ color: #e00;
+ font-weight: 700;
+ padding: 3px;
+}
+a:only-of-type > .remove {
+ display: none;
+}
+.remove:hover::after {
+ content: " Remove";
+}
+.qr-preview > label {
+ background: rgba(0,0,0,.5);
+ color: #fff;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ text-align: center;
+}
+.qr-preview > label > input {
+ margin: 0;
+}
+#add-post {
+ cursor: pointer;
+ font-size: 2em;
+ position: absolute;
+ top: 50%;
+ right: 10px;
+ -moz-transform: translateY(-50%);
+}
+.textarea {
+ position: relative;
+}
+:root.webkit .textarea {
+ margin-bottom: -2px;
+}
+#char-count {
+ color: #000;
+ background: hsla(0, 0%, 100%, .5);
+ font-size: 8pt;
+ position: absolute;
+ bottom: 1px;
+ right: 1px;
+ pointer-events: none;
+}
+
+/* Menu */
+.menu-button {
+ display: inline-block;
+ position: relative;
+ cursor: pointer;
+}
+.menu-button i {
+ border-top: 6px solid;
+ border-right: 4px solid transparent;
+ border-left: 4px solid transparent;
+ display: inline-block;
+ margin: 2px;
+ vertical-align: middle;
+}
+#menu {
+ position: fixed;
+ outline: none;
+}
+.entry {
+ border-bottom: 1px solid rgba(0,0,0,.25);
+ cursor: pointer;
+ display: block;
+ outline: none;
+ padding: 3px 7px;
+ position: relative;
+ text-decoration: none;
+ white-space: nowrap;
+ min-width: 70px;
+}
+.left>.entry.has-submenu {
+ padding-right: 17px !important;
+}
+.entry:last-child {
+ border-bottom: 0;
+}
+.has-submenu::after {
+ content: "";
+ border-left: .5em solid;
+ border-top: .3em solid transparent;
+ border-bottom: .3em solid transparent;
+ display: inline-block;
+ margin: .3em;
+ position: absolute;
+ right: 3px;
+}
+.left .has-submenu::after {
+ border-left: 0;
+ border-right: .5em solid;
+}
+.submenu {
+ display: none;
+ position: absolute;
+ left: 100%;
+ top: -1px;
+}
+.focused .submenu {
+ display: block;
+}
+.imp-exp-result {
+ position: absolute;
+ text-align: center;
+ margin: auto;
+ right: 0px;
+ left: 0px;
+ width: 200px;
+}
+.export, .import {
+ cursor: pointer;
+ text-decoration: none !important;
+}
+/* Custom Board Titles */
+.boardTitle[contenteditable="true"],
+.boardSubtitle[contenteditable="true"] {
+ cursor: text !important;
+}
+/* Link Title Favicons */
+.linkify.YouTube {
+ background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/youtube.png", {encoding: "base64"}) %>') center left no-repeat!important;
+ padding-left: 18px;
+}
+.linkify.Vimeo {
+ background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/vimeo.png", {encoding: "base64"}) %>') center left no-repeat!important;
+ padding-left: 18px;
+}
+.linkify.SoundCloud {
+ background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/soundcloud.png", {encoding: "base64"}) %>') center left no-repeat!important;
+ padding-left: 18px;
+}
+.linkify.audio {
+ background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/audio.png", {encoding: "base64"}) %>') center left no-repeat!important;
+ padding-left: 18px;
+}
+.linkify.LiveLeak {
+ background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/liveleak.png", {encoding: "base64"}) %>') center left no-repeat!important;
+ padding-left: 18px;
+}
+.linkify.Vocaroo {
+ background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/vocaroo.png", {encoding: "base64"}) %>') center left no-repeat!important;
+ padding-left: 18px;
+}
+.linkify.pastebin {
+ background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/pastebin.png", {encoding: "base64"}) %>') center left no-repeat!important;
+ padding-left: 18px;
+}
+.linkify.gist {
+ background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/gist.png", {encoding: "base64"}) %>') center left no-repeat!important;
+ padding-left: 18px;
+}
+.linkify.image {
+ background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/image.png", {encoding: "base64"}) %>') center left no-repeat!important;
+ padding-left: 18px;
+}
+.linkify.InstallGentoo {
+ background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/installgentoo.png", {encoding: "base64"}) %>') center left no-repeat!important;
+ padding-left: 18px;
+}
+
+/* Gallery */
+#a-gallery {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 30;
+ display: <%= flex %>;
+ <%= flex %>-direction: row;
+ background: rgba(0,0,0,0.7);
+}
+.gal-viewport {
+ display: <%= flex %>;
+ <%= align %>-items: stretch;
+ <%= flex %>-direction: row;
+ <%= flex %>: 1 1 auto;
+}
+.gal-thumbnails {
+ <%= flex %>: 0 0 150px;
+ overflow-y: auto;
+ display: <%= flex %>;
+ <%= flex %>-direction: column;
+ <%= align %>-items: stretch;
+ text-align: center;
+ background: rgba(0,0,0,.5);
+ border-left: 1px solid #222;
+}
+.gal-hide-thumbnails .gal-thumbnails {
+ display: none;
+}
+.gal-thumb img {
+ max-width: 125px;
+ max-height: 125px;
+ height: auto;
+ width: auto;
+}
+.gal-thumb {
+ <%= flex %>: 0 0 auto;
+ padding: 3px;
+ line-height: 0;
+ transition: background .2s linear;
+}
+.gal-highlight {
+ background: rgba(0, 190, 255,.8);
+}
+.gal-prev {
+ order: 0;
+ border-right: 1px solid #222;
+}
+.gal-next {
+ order: 2;
+ border-left: 1px solid #222;
+}
+.gal-prev,
+.gal-next {
+ <%= flex %>: 0 0 20px;
+ position: relative;
+ cursor: pointer;
+ opacity: 0.7;
+ background-color: rgba(0, 0, 0, 0.3);
+}
+.gal-prev:hover,
+.gal-next:hover {
+ opacity: 1;
+}
+.gal-prev::after,
+.gal-next::after {
+ position: absolute;
+ top: 48.6%;
+ <%= transform %>: translateY(-50%)
+ display: inline-block;
+ border-top: 11px solid transparent;
+ border-bottom: 11px solid transparent;
+ content: "";
+}
+.gal-prev::after {
+ border-right: 12px solid #fff;
+ right: 5px;
+}
+.gal-next::after {
+ border-left: 12px solid #fff;
+ right: 3px;
+}
+.gal-image {
+ order: 1;
+ <%= flex %>: 1 0 auto;
+ display: <%= flex %>;
+ <%= align %>-items: flex-start;
+ <%= justify %>: space-around;
+ overflow: hidden;
+ /* Flex > Non-Flex child max-width and overflow fix (Firefox only?) */
+ width: 1%;
+}
+:root:not(.gal-fit-height) .gal-image {
+ overflow-y: scroll !important;
+}
+:root:not(.gal-fit-width) .gal-image {
+ overflow-x: scroll !important;
+}
+.gal-image a {
+ margin: auto;
+ line-height: 0;
+}
+.gal-fit-width .gal-image img {
+ max-width: 100%;
+}
+.gal-fit-height .gal-image img {
+ /*
+ Chrome doesn't support viewpoint units in calc()
+ http://bugs.chromium.org/168840
+ "It looks like the original author of viewport units in WebKit is not coming back to fix this stuff."
+ Well, fuck.
+ */
+ max-height: 95vh;
+ max-height: calc(100vh - 25px);
+}
+.gal-buttons {
+ font-size: 2em;
+ margin-right: 10px;
+ top: 5px;
+}
+.gal-buttons i {
+ vertical-align: baseline;
+ border-top-width: .4em;
+ border-right-width: .25em;
+ border-left-width: .25em;
+}
+.gal-buttons .menu-button {
+ bottom: 2px;
+ color: #ffffff;
+ text-shadow: 0px 0px 1px #000000;
+}
+.gal-close {
+ color: #ffffff;
+ text-shadow: 0px 0px 1px #000000;
+}
+.gal-buttons,
+.gal-name,
+.gal-count {
+ position: fixed;
+ right: 178px;
+}
+.gal-hide-thumbnails .gal-buttons,
+.gal-hide-thumbnails .gal-count,
+.gal-hide-thumbnails .gal-name {
+ right: 28px;
+}
+.gal-name {
+ bottom: 6px;
+ background: rgba(0,0,0,0.6) !important;
+ border-radius: 3px;
+ padding: 1px 5px 2px 5px;
+ text-decoration: none !important;
+ color: white !important;
+}
+.gal-name:hover,
+.gal-close:hover,
+.gal-buttons .menu-button:hover {
+ color: rgb(95, 95, 101) !important;
+}
+.gal-count {
+ bottom: 27px;
+ background: rgba(0,0,0,0.6) !important;
+ border-radius: 3px;
+ padding: 1px 5px 2px 5px;
+ color: #ffffff !important;
+}
+:root:not(.gal-fit-width) .gal-name {
+ bottom: 23px !important;
+}
+:root:not(.gal-fit-width) .gal-count {
+ bottom: 44px !important;
+}
+:root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-buttons,
+:root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-name,
+:root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-count {
+ right: 195px !important;
+}
+:root.gal-hide-thumbnails:not(.gal-fit-height) .gal-buttons,
+:root.gal-hide-thumbnails:not(.gal-fit-height) .gal-name,
+:root.gal-hide-thumbnails:not(.gal-fit-height) .gal-count {
+ right: 44px !important;
+}
\ No newline at end of file
diff --git a/src/General/html/Build/post.html b/src/General/html/Build/post.html
index 234b356f4..395255057 100755
--- a/src/General/html/Build/post.html
+++ b/src/General/html/Build/post.html
@@ -34,7 +34,7 @@
- #{subject}
+ #{subject}
#{emailStart}
#{name or ''}
diff --git a/src/General/lib/post.class b/src/General/lib/post.class
index 6e7bffd2d..65ace17aa 100755
--- a/src/General/lib/post.class
+++ b/src/General/lib/post.class
@@ -68,13 +68,12 @@ class Post
#
-> \n
# Remove:
# 'Comment too long'...
- # Admin/Mod/Dev replies. (/q/)
# EXIF data. (/p/)
# Rolls. (/tg/)
# Preceding and following new lines.
# Trailing spaces.
bq = @nodes.comment.cloneNode true
- nodes = $$ '.abbr, .capcodeReplies, .exif, b', bq
+ nodes = $$ '.abbr, .exif, b', bq
i = 0
while node = nodes[i++]
$.rm node
@@ -108,8 +107,7 @@ class Post
@nodes.quotelinks.push quotelink
- # Don't count capcode replies as quotes in OPs. (Admin/Mod/Dev Replies: ...)
- return if @isClone or !@isReply and $.hasClass quotelink.parentNode.parentNode, 'capcodeReplies'
+ return if @isClone
# ES6 Set when?
fullID = "#{match[1]}.#{match[2]}"
diff --git a/src/Menu/DeleteLink.coffee b/src/Menu/DeleteLink.coffee
index 1912733c6..1805047f8 100755
--- a/src/Menu/DeleteLink.coffee
+++ b/src/Menu/DeleteLink.coffee
@@ -31,7 +31,7 @@ DeleteLink =
el: div
order: 40
open: (post) ->
- return false if post.isDead or post.board.ID is 'q'
+ return false if post.isDead
DeleteLink.post = post
node = div.firstChild
node.textContent = 'Delete'
@@ -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/Miscellaneous/ExpandComment.coffee b/src/Miscellaneous/ExpandComment.coffee
index d06f140c2..c256df59b 100755
--- a/src/Miscellaneous/ExpandComment.coffee
+++ b/src/Miscellaneous/ExpandComment.coffee
@@ -60,11 +60,6 @@ ExpandComment =
href = quote.getAttribute 'href'
continue if href[0] is '/' # Cross-board quote, or board link
quote.href = "/#{post.board}/res/#{href}" # Fix pathnames
- Build.capcodeReplies
- boardID: post.board.ID
- threadID: post.thread.ID
- bq: clone
- capcodeReplies: postObj.capcode_replies
post.nodes.shortComment = comment
$.replace comment, clone
post.nodes.comment = post.nodes.longComment = clone
diff --git a/src/Miscellaneous/ExpandThread.coffee b/src/Miscellaneous/ExpandThread.coffee
index 504fb1125..5ea898fc7 100755
--- a/src/Miscellaneous/ExpandThread.coffee
+++ b/src/Miscellaneous/ExpandThread.coffee
@@ -54,7 +54,7 @@ ExpandThread =
1
else switch g.BOARD.ID
# XXX boards config
- when 'b', 'vg', 'q' then 3
+ when 'b', 'vg' then 3
when 't' then 1
else 5
posts = $$ ".thread > .replyContainer", threadRoot
diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee
index 0265197f1..0607d258a 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: "[]\u00A0"
@@ -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']
@@ -82,17 +80,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) ->
@@ -111,28 +108,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()
@@ -162,8 +153,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
@@ -177,8 +166,7 @@ ThreadUpdater =
else
i
ThreadUpdater.set 'timer', ThreadUpdater.seconds++
- clearTimeout ThreadUpdater.timeoutID
- ThreadUpdater.timeout()
+ ThreadUpdater.count true
intervalShortcut: ->
Settings.open 'Advanced'
@@ -195,14 +183,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 5b032b0f4..42352094b 100755
--- a/src/Monitoring/ThreadWatcher.coffee
+++ b/src/Monitoring/ThreadWatcher.coffee
@@ -19,14 +19,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 5b42328b0..79aefcfce 100644
--- a/src/Posting/QuickReply.coffee
+++ b/src/Posting/QuickReply.coffee
@@ -268,27 +268,26 @@ 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:
init: ->
return unless Conf['Cooldown']
- board = g.BOARD.ID
- QR.cooldown.types =
- thread: switch board
- when 'q' then 86400
- when 'b', 'soc', 'r9k' then 600
- else 300
- sage: if board is 'q' then 600 else 60
- file: if board is 'q' then 300 else 30
- post: if board is 'q' then 150 else 30
+ setTimers = (e) => QR.cooldown.types = e.detail
+ $.on window, 'cooldown:timers', setTimers
+ $.globalEval 'window.dispatchEvent(new CustomEvent("cooldown:timers", {detail: cooldowns}))'
+ QR.cooldown.types or= {} # XXX tmp workaround until all pages and the catalogs get the cooldowns var.
+ $.off window, 'cooldown:timers', setTimers
+ for type of QR.cooldown.types
+ QR.cooldown.types[type] = +QR.cooldown.types[type]
QR.cooldown.upSpd = 0
QR.cooldown.upSpdAccuracy = .5
- $.get "cooldown.#{board}", {}, (item) ->
- QR.cooldown.cooldowns = item["cooldown.#{board}"]
+ key = "cooldown.#{g.BOARD}"
+ $.get key, {}, (item) ->
+ QR.cooldown.cooldowns = item[key]
QR.cooldown.start()
- $.sync "cooldown.#{board}", QR.cooldown.sync
-
+ $.sync key, QR.cooldown.sync
start: ->
return unless Conf['Cooldown']
return if QR.cooldown.isCounting
@@ -304,30 +303,16 @@ QR =
set: (data) ->
return unless Conf['Cooldown']
- {req, post, isReply, delay} = data
+ {req, post, isReply, threadID, delay} = data
start = if req then req.uploadEndTime else Date.now()
if delay
cooldown = {delay}
else
- if post.file
- upSpd = post.file.size / ((req.uploadEndTime - req.uploadStartTime) / $.SECOND)
+ 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
- isSage = /sage/i.test post.email
- hasFile = !!post.file
- type = unless isReply
- 'thread'
- else if isSage
- 'sage'
- else if hasFile
- 'file'
- else
- 'post'
- cooldown =
- isReply: isReply
- isSage: isSage
- hasFile: hasFile
- timeout: start + QR.cooldown.types[type] * $.SECOND
+ cooldown = {isReply, hasFile, threadID}
QR.cooldown.cooldowns[start] = cooldown
$.set "cooldown.#{g.BOARD}", QR.cooldown.cooldowns
QR.cooldown.start()
@@ -347,12 +332,12 @@ QR =
QR.status()
return
- setTimeout QR.cooldown.count, $.SECOND
+ clearTimeout QR.cooldown.timeout
+ QR.cooldown.timeout = setTimeout QR.cooldown.count, $.SECOND
- now = Date.now()
- post = QR.posts[0]
+ now = Date.now()
+ post = QR.posts[0]
isReply = post.thread isnt 'new'
- isSage = /sage/i.test post.email
hasFile = !!post.file
seconds = null
{types, cooldowns, upSpd, upSpdAccuracy} = QR.cooldown
@@ -366,26 +351,35 @@ QR =
QR.cooldown.unset start
continue
- if isReply is cooldown.isReply
- # Only cooldowns relevant to this post can set the seconds value.
- # Unset outdated cooldowns that can no longer impact us.
- type = unless isReply
- 'thread'
- else if isSage and cooldown.isSage
- 'sage'
- else if hasFile and cooldown.hasFile
- 'file'
- else
- 'post'
- elapsed = Math.floor (now - start) / $.SECOND
- if elapsed >= 0 # clock changed since then?
- seconds = Math.max seconds, types[type] - elapsed
- if Conf['Cooldown Prediction'] and hasFile and upSpd
- seconds -= Math.floor post.file.size / upSpd * upSpdAccuracy
- seconds = Math.max seconds, 0
- unless start <= now <= cooldown.timeout
+ if 'timeout' of cooldown
+ # XXX tmp conversion from previous cooldowns
QR.cooldown.unset start
+ continue
+ if isReply is cooldown.isReply
+ # Only cooldowns relevant to this post can set the seconds variable:
+ # reply cooldown with a reply, thread cooldown with a thread
+ elapsed = Math.floor (now - start) / $.SECOND
+ continue if elapsed < 0 # clock changed since then?
+ unless isReply
+ type = 'thread'
+ else if hasFile
+ # You can post an image reply immediately after a non-image reply.
+ unless cooldown.hasFile
+ seconds = Math.max seconds, 0
+ continue
+ type = 'image'
+ else
+ 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
+ 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 = 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.
@@ -575,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()
@@ -596,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
@@ -623,8 +623,10 @@ 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
QR.tripcodeHider.call QR.nodes['name']
@showFileData()
@@ -635,7 +637,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()
@@ -660,8 +662,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) ->
@@ -966,7 +969,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
@@ -1006,10 +1015,13 @@ QR =
$.on nodes[name], 'mouseover', QR.mouseover
# 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 @
+ continue unless node = nodes[name]
+ event = if node.nodeName is 'SELECT' then 'change' else 'input'
+ $.on nodes[name], event, save
$.on nodes['name'], 'blur', QR.tripcodeHider
$.on nodes.thread, 'change', -> QR.selected.save @
@@ -1068,7 +1080,7 @@ QR =
# prevent errors
if threadID is 'new'
threadID = null
- if ['vg', 'q'].contains(g.BOARD.ID) 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.'
@@ -1104,7 +1116,7 @@ QR =
post.lock()
- postData =
+ formData =
resto: threadID
name: post.name
email: post.email
@@ -1113,6 +1125,7 @@ QR =
upfile: post.file
filetag: filetag
spoiler: post.spoiler
+ flag: post.flag
textonly: textOnly
mode: 'regist'
pwd: QR.persona.pwd
@@ -1136,7 +1149,7 @@ QR =
[?]
"""
extra =
- form: $.formData postData
+ form: $.formData formData
upCallbacks:
onload: ->
# Upload done, waiting for server response.
@@ -1241,9 +1254,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}."
@@ -1262,7 +1275,7 @@ QR =
else
post.rm()
- QR.cooldown.set {req, post, isReply}
+ QR.cooldown.set {req, post, isReply, threadID}
URL = unless isReply # new thread
"/#{g.BOARD}/res/#{threadID}"