Merge branch 'v3'
Conflicts: CHANGELOG.md Gruntfile.coffee LICENSE builds/4chan-X.meta.js builds/4chan-X.user.js builds/crx/manifest.json builds/crx/script.js latest.js package.json src/General/css/style.css src/Posting/QuickReply.coffee
This commit is contained in:
commit
7812001d96
24
CHANGELOG.md
24
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*
|
||||
|
||||
|
||||
@ -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'
|
||||
|
||||
2
LICENSE
2
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
|
||||
|
||||
@ -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 ? '' : "<div class=sideArrows id=sa" + postID + ">>></div>") + "<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'><div class='postInfoM mobile' id=pim" + postID + "><span class='nameBlock" + capcodeClass + "'><span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "<br>" + subject + "</span><span class='dateTime postNum' data-utc=" + dateUTC + ">" + date + "<a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + ">No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "'>" + postID + "</a></span></div>" + (isOP ? fileHTML : '') + "<div class='postInfo desktop' id=pi" + postID + "><input type=checkbox name=" + postID + " value=delete>" + subject + " <span class='nameBlock" + capcodeClass + "'>" + emailStart + "<span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "</span>" + " " + "<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span>" + " " + "<span class='postNum desktop'><a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a></span></div>" + (isOP ? '' : fileHTML) + "<blockquote class=postMessage id=m" + postID + ">" + (comment || '') + "</blockquote>" + " " + "</div>"
|
||||
innerHTML: "" + (isOP ? '' : "<div class=sideArrows id=sa" + postID + ">>></div>") + "<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'><div class='postInfoM mobile' id=pim" + postID + "><span class='nameBlock" + capcodeClass + "'><span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "<br>" + subject + "</span><span class='dateTime postNum' data-utc=" + dateUTC + ">" + date + "<a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + ">No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "'>" + postID + "</a></span></div>" + (isOP ? fileHTML : '') + "<div class='postInfo desktop' id=pi" + postID + "><input type=checkbox name=" + postID + " value=delete> " + subject + " <span class='nameBlock" + capcodeClass + "'>" + emailStart + "<span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "</span>" + " " + "<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span>" + " " + "<span class='postNum desktop'><a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a></span></div>" + (isOP ? '' : fileHTML) + "<blockquote class=postMessage id=m" + postID + ">" + (comment || '') + "</blockquote>" + " " + "</div>"
|
||||
});
|
||||
_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 "<span class=smaller><span class=bold>" + ((function() {
|
||||
switch (capcodeType) {
|
||||
case 'admin':
|
||||
return 'Administrator';
|
||||
case 'mod':
|
||||
return 'Moderator';
|
||||
case 'developer':
|
||||
return 'Developer';
|
||||
}
|
||||
})()) + " Repl" + (array.length > 1 ? 'ies' : 'y') + ":</span> " + (array.map(function(ID) {
|
||||
return "<a href='/" + boardID + "/res/" + threadID + "#p" + ID + "' class=quotelink>>>" + ID + "</a>";
|
||||
}).join(' ')) + "</span><br>";
|
||||
};
|
||||
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;', "<div id=qrtab class=move><input type=checkbox id=autohide title=Auto-hide><div id=qr-thread-select><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><a href=javascript:; class=close title=Close>✖</a></div><form><div class=persona><input name=name data-name=name list=\"list-name\" placeholder=Name class=field size=1 tabindex=10><input name=email data-name=email list=\"list-email\" placeholder=E-mail class=field size=1 tabindex=20><input name=sub data-name=sub list=\"list-sub\" placeholder=Subject class=field size=1 tabindex=30></div><div class=textarea><textarea data-name=com placeholder=Comment class=field tabindex=40></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\" tabindex=50>+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=60><span id=qr-no-file>No selected file</span><input id=\"qr-filename\" data-name=\"filename\" spellcheck=\"false\"><span id=qr-extras-container><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=70></label><span class=description>Spoiler</span><a id=dump-button title='Dump list'>+</a><span class=description>Dump</span><a id=qr-filerm href=javascript:; title='Remove file'>✖</a><span class=description>Remove File</span></span></span><input type=submit tabindex=80></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist>")
|
||||
@ -8462,8 +8438,15 @@
|
||||
name: 'filetag',
|
||||
innerHTML: "<option value=0>Hentai</option>\n<option value=6>Porn</option>\n<option value=1>Japanese</option>\n<option value=2>Anime</option>\n<option value=3>Game</option>\n<option value=5>Loop</option>\n<option value=4 selected>Other</option>"
|
||||
});
|
||||
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: "[<span id=update-status></span><span id=update-timer title='Update now'></span>]\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;
|
||||
|
||||
@ -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 ? '' : "<div class=sideArrows id=sa" + postID + ">>></div>") + "<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'><div class='postInfoM mobile' id=pim" + postID + "><span class='nameBlock" + capcodeClass + "'><span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "<br>" + subject + "</span><span class='dateTime postNum' data-utc=" + dateUTC + ">" + date + "<a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + ">No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "'>" + postID + "</a></span></div>" + (isOP ? fileHTML : '') + "<div class='postInfo desktop' id=pi" + postID + "><input type=checkbox name=" + postID + " value=delete>" + subject + " <span class='nameBlock" + capcodeClass + "'>" + emailStart + "<span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "</span>" + " " + "<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span>" + " " + "<span class='postNum desktop'><a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a></span></div>" + (isOP ? '' : fileHTML) + "<blockquote class=postMessage id=m" + postID + ">" + (comment || '') + "</blockquote>" + " " + "</div>"
|
||||
innerHTML: "" + (isOP ? '' : "<div class=sideArrows id=sa" + postID + ">>></div>") + "<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'><div class='postInfoM mobile' id=pim" + postID + "><span class='nameBlock" + capcodeClass + "'><span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "<br>" + subject + "</span><span class='dateTime postNum' data-utc=" + dateUTC + ">" + date + "<a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + ">No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "'>" + postID + "</a></span></div>" + (isOP ? fileHTML : '') + "<div class='postInfo desktop' id=pi" + postID + "><input type=checkbox name=" + postID + " value=delete> " + subject + " <span class='nameBlock" + capcodeClass + "'>" + emailStart + "<span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag + sticky + closed) + "</span>" + " " + "<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span>" + " " + "<span class='postNum desktop'><a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a></span></div>" + (isOP ? '' : fileHTML) + "<blockquote class=postMessage id=m" + postID + ">" + (comment || '') + "</blockquote>" + " " + "</div>"
|
||||
});
|
||||
_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 "<span class=smaller><span class=bold>" + ((function() {
|
||||
switch (capcodeType) {
|
||||
case 'admin':
|
||||
return 'Administrator';
|
||||
case 'mod':
|
||||
return 'Moderator';
|
||||
case 'developer':
|
||||
return 'Developer';
|
||||
}
|
||||
})()) + " Repl" + (array.length > 1 ? 'ies' : 'y') + ":</span> " + (array.map(function(ID) {
|
||||
return "<a href='/" + boardID + "/res/" + threadID + "#p" + ID + "' class=quotelink>>>" + ID + "</a>";
|
||||
}).join(' ')) + "</span><br>";
|
||||
};
|
||||
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;', "<div id=qrtab class=move><input type=checkbox id=autohide title=Auto-hide><div id=qr-thread-select><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><a href=javascript:; class=close title=Close>✖</a></div><form><div class=persona><input name=name data-name=name list=\"list-name\" placeholder=Name class=field size=1 tabindex=10><input name=email data-name=email list=\"list-email\" placeholder=E-mail class=field size=1 tabindex=20><input name=sub data-name=sub list=\"list-sub\" placeholder=Subject class=field size=1 tabindex=30></div><div class=textarea><textarea data-name=com placeholder=Comment class=field tabindex=40></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\" tabindex=50>+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=60><span id=qr-no-file>No selected file</span><input id=\"qr-filename\" data-name=\"filename\" spellcheck=\"false\"><span id=qr-extras-container><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=70></label><span class=description>Spoiler</span><a id=dump-button title='Dump list'>+</a><span class=description>Dump</span><a id=qr-filerm href=javascript:; title='Remove file'>✖</a><span class=description>Remove File</span></span></span><input type=submit tabindex=80></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist>")
|
||||
@ -8469,8 +8445,15 @@
|
||||
name: 'filetag',
|
||||
innerHTML: "<option value=0>Hentai</option>\n<option value=6>Porn</option>\n<option value=1>Japanese</option>\n<option value=2>Anime</option>\n<option value=3>Game</option>\n<option value=5>Loop</option>\n<option value=4 selected>Other</option>"
|
||||
});
|
||||
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: "[<span id=update-status></span><span id=update-timer title='Update now'></span>]\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;
|
||||
|
||||
@ -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;
|
||||
|
||||
110
json/archives.json
Normal file
110
json/archives.json
Normal file
@ -0,0 +1,110 @@
|
||||
[{
|
||||
"uid": 0,
|
||||
"name": "Foolz",
|
||||
"domain": "archive.foolz.us",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["a", "co", "gd", "jp", "m", "q", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"],
|
||||
"files": ["a", "gd", "jp", "m", "q", "tg", "vg", "vp", "vr", "wsg"]
|
||||
}, {
|
||||
"uid": 1,
|
||||
"name": "NSFW Foolz",
|
||||
"domain": "nsfw.foolz.us",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["u"],
|
||||
"files": ["u"]
|
||||
}, {
|
||||
"uid": 2,
|
||||
"name": "The Dark Cave",
|
||||
"domain": "archive.thedarkcave.org",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["c", "int", "out", "po"],
|
||||
"files": ["c", "po"]
|
||||
}, {
|
||||
"uid": 3,
|
||||
"name": "4plebs",
|
||||
"domain": "archive.4plebs.org",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["hr", "tg", "tv", "x"],
|
||||
"files": ["hr", "tg", "tv", "x"]
|
||||
}, {
|
||||
"uid": 4,
|
||||
"name": "Nyafuu",
|
||||
"domain": "archive.nyafuu.org",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["c", "w", "wg"],
|
||||
"files": ["c", "w", "wg"]
|
||||
}, {
|
||||
"uid": 11,
|
||||
"name": "Foolz a Shit",
|
||||
"domain": "archive.foolzashit.com",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["adv", "asp", "cm", "d", "e", "i", "lgbt", "n", "o", "p", "pol", "s", "s4s", "t", "trv", "y"],
|
||||
"files": ["cm", "d", "e", "i", "n", "o", "p", "s", "trv", "y"]
|
||||
}, {
|
||||
"uid": 12,
|
||||
"name": "FapArchive",
|
||||
"domain": "fuuka.worldathleticproject.org",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["b", "e", "h", "hc", "p", "s", "soc", "sp", "u"],
|
||||
"files": ["b", "e", "h", "hc", "p", "s", "soc", "sp", "u"]
|
||||
}, {
|
||||
"uid": 7,
|
||||
"name": "Install Gentoo",
|
||||
"domain": "archive.installgentoo.net",
|
||||
"http": false,
|
||||
"https": true,
|
||||
"software": "fuuka",
|
||||
"boards": ["diy", "g", "sci"],
|
||||
"files": []
|
||||
}, {
|
||||
"uid": 8,
|
||||
"name": "Rebecca Black Tech",
|
||||
"domain": "rbt.asia",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "fuuka",
|
||||
"boards": ["cgl", "g", "mu", "w"],
|
||||
"files": ["cgl", "g", "mu", "w"]
|
||||
}, {
|
||||
"uid": 9,
|
||||
"name": "Heinessen",
|
||||
"domain": "archive.heinessen.com",
|
||||
"http": true,
|
||||
"https": false,
|
||||
"software": "fuuka",
|
||||
"boards": ["an", "fit", "k", "mlp", "r9k", "toy"],
|
||||
"files": ["an", "fit", "k", "r9k", "toy"]
|
||||
}, {
|
||||
"uid": 10,
|
||||
"name": "warosu",
|
||||
"domain": "fuuka.warosu.org",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "fuuka",
|
||||
"boards": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"],
|
||||
"files": ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "q", "tg", "vr"]
|
||||
}, {
|
||||
"uid": 13,
|
||||
"name": "Foolz Beta",
|
||||
"domain": "beta.foolz.us",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"withCredentials": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["a", "co", "d", "gd", "h", "jp", "m", "mlp", "q", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
|
||||
"files": ["a", "d", "gd", "h", "jp", "m", "q", "tg", "u", "vg", "vp", "vr", "wsg"]
|
||||
}]
|
||||
@ -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": {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
$.replace email, name
|
||||
delete @nodes.email
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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})"
|
||||
|
||||
@ -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) ->
|
||||
"<span class=smaller><span class=bold>#{
|
||||
switch capcodeType
|
||||
when 'admin'
|
||||
'Administrator'
|
||||
when 'mod'
|
||||
'Moderator'
|
||||
when 'developer'
|
||||
'Developer'
|
||||
} Repl#{if array.length > 1 then 'ies' else 'y'}:</span> #{
|
||||
array.map (ID) ->
|
||||
"<a href='/#{boardID}/res/#{threadID}#p#{ID}' class=quotelink>>>#{ID}</a>"
|
||||
.join ' '
|
||||
}</span><br>"
|
||||
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 ''
|
||||
]
|
||||
|
||||
@ -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
|
||||
|
||||
1271
src/General/css/style.css
Executable file
1271
src/General/css/style.css
Executable file
File diff suppressed because it is too large
Load Diff
@ -34,7 +34,7 @@
|
||||
|
||||
<div class='postInfo desktop' id=pi#{postID}>
|
||||
<input type=checkbox name=#{postID} value=delete>
|
||||
#{subject}
|
||||
#{subject}
|
||||
<span class='nameBlock#{capcodeClass}'>
|
||||
#{emailStart}
|
||||
<span class=name>#{name or ''}</span>
|
||||
|
||||
@ -68,13 +68,12 @@ class Post
|
||||
# <br> -> \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]}"
|
||||
|
||||
@ -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) ->
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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: "[<span id=update-status></span><span id=update-timer title='Update now'></span>]\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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 =
|
||||
<option value=5>Loop</option>
|
||||
<option value=4 selected>Other</option>
|
||||
"""
|
||||
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 =
|
||||
[<a href="https://github.com/seaweedchan/4chan-x/wiki/Frequently-Asked-Questions#what-does-4chan-x-encountered-an-error-while-posting-please-try-again-mean" target=_blank>?</a>]
|
||||
"""
|
||||
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}"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user