diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8eb6d767e..1f37e3371 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,17 @@
-Sometimes the changelog has notes (not comprehensive) acknowledging people's work. This does not mean the changes are their fault, only that their code was used. All changes to the script are chosen by and the fault of the maintainer (ccd0).
+### v1.13.5
+
+**v1.13.5.0** *(2017-01-24)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.5.0/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.5.0/builds/4chan-X-noupdate.crx)]
+- Based on v1.13.4.1.
+- Bring back `Archive Report` feature, now with reporting to multiple archives through the offsite reports API of https://github.com/pleebe/foolfuuka-plugin-popup-report. #1260
+- Add filename regular expression matching to Sauce. Sauce will now recognize Pixiv, DeviantArt, Imgur, Flickr, and Facebook filenames and link to the page the image came from. #1183
+- Update regex.info -> exif.regex.info in Sauce links.
+- Parameters in Sauce links will only be expanded in the URL and displayed text.
+- Posts fetched from an archive now have their file links point to the archive chosen by the user for file redirection, rather than the URL given by the archive. #1255
+- Minor bugfixes.
+
### v1.13.4
**v1.13.4.1** *(2017-01-07)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.4.1/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.4.1/builds/4chan-X-noupdate.crx)]
diff --git a/builds/4chan-X-beta.crx b/builds/4chan-X-beta.crx
index 031a5cc95..b980d87a5 100644
Binary files a/builds/4chan-X-beta.crx and b/builds/4chan-X-beta.crx differ
diff --git a/builds/4chan-X-beta.meta.js b/builds/4chan-X-beta.meta.js
index c8107ea4f..c8c774b9e 100644
--- a/builds/4chan-X-beta.meta.js
+++ b/builds/4chan-X-beta.meta.js
@@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X beta
-// @version 1.13.4.1
+// @version 1.13.5.0
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
diff --git a/builds/4chan-X-beta.user.js b/builds/4chan-X-beta.user.js
index 581c370b9..57cc85f40 100644
--- a/builds/4chan-X-beta.user.js
+++ b/builds/4chan-X-beta.user.js
@@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X beta
-// @version 1.13.4.1
+// @version 1.13.5.0
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@@ -150,7 +150,7 @@ docSet = function() {
};
g = {
- VERSION: '1.13.4.1',
+ VERSION: '1.13.5.0',
NAMESPACE: '4chan X.',
boards: {}
};
@@ -202,6 +202,7 @@ Config = (function() {
'Announcement Hiding': [true, 'Add button to hide 4chan announcements.'],
'Desktop Notifications': [true, 'Enables desktop notifications across various 4chan X features.'],
'404 Redirect': [true, 'Redirect dead threads and images to the archives.'],
+ 'Archive Report': [true, 'Enable reporting posts to supported archives.'],
'Exempt Archives from Encryption': [true, 'Permit loading content from, and warningless redirects to, HTTP-only archives from HTTPS pages.'],
'Keybinds': [true, 'Bind actions to keyboard shortcuts.'],
'Time Formatting': [true, 'Localize and format timestamps.'],
@@ -379,7 +380,7 @@ Config = (function() {
filesize: '',
MD5: ''
},
- sauces: "# Reverse image search:\nhttps://www.google.com/searchbyimage?image_url=%IMG&safe=off\nhttps://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//tineye.com/search?url=%IMG\n#//www.bing.com/images/search?q=imgurl:%IMG&view=detailv2&iss=sbi#enterInsights\n\n# Specialized reverse image search:\n//iqdb.org/?url=%IMG\nhttps://whatanime.ga/?auto&url=%IMG;text:wait\n#//3d.iqdb.org/?url=%IMG\n#//saucenao.com/search.php?url=%IMG\n\n# \"View Same\" in archives:\nhttp://eye.swfchan.com/search/?q=%name;types:swf\n#https://desuarchive.org/_/search/image/%sMD5/\n#https://archive.4plebs.org/_/search/image/%sMD5/\n#https://boards.fireden.net/_/search/image/%sMD5/\n#https://foolz.fireden.net/_/search/image/%sMD5/\n\n# Other tools:\n#http://regex.info/exif.cgi?imgurl=%URL\n#//imgops.com/%URL;types:gif,jpg,png\n#//www.gif-explode.com/%URL;types:gif",
+ sauces: "# Known filename formats:\nhttp://www.pixiv.net/member_illust.php?mode=medium&illust_id=%$1;regexp:/^(\\d+)_p\\d+/\n//%$1.deviantart.com/gallery/#/d%$2;regexp:/^\\w+_by_(\\w+)-d([\\da-z]+)/\n//imgur.com/%$1;regexp:/^(?![a-zA-Z][a-z]{6})(?![A-Z]{7})(?!\\d{7})([\\da-zA-Z]{7})(?: \\(\\d+\\))?\\.\\w+$/\nhttp://flickr.com/photo.gne?id=%$1;regexp:/^(\\d+)_[\\da-f]{10}(?:_\\w)*\\b/\nhttps://www.facebook.com/photo.php?fbid=%$1;regexp:/^\\d+_(\\d+)_\\d+_[no]\\b/\n\n# Reverse image search:\nhttps://www.google.com/searchbyimage?image_url=%IMG&safe=off\nhttps://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//tineye.com/search?url=%IMG\n#//www.bing.com/images/search?q=imgurl:%IMG&view=detailv2&iss=sbi#enterInsights\n\n# Specialized reverse image search:\n//iqdb.org/?url=%IMG\nhttps://whatanime.ga/?auto&url=%IMG;text:wait\n#//3d.iqdb.org/?url=%IMG\n#//saucenao.com/search.php?url=%IMG\n\n# \"View Same\" in archives:\nhttp://eye.swfchan.com/search/?q=%name;types:swf\n#https://desuarchive.org/_/search/image/%sMD5/\n#https://archive.4plebs.org/_/search/image/%sMD5/\n#https://boards.fireden.net/_/search/image/%sMD5/\n#https://foolz.fireden.net/_/search/image/%sMD5/\n\n# Other tools:\n#http://exif.regex.info/exif.cgi?imgurl=%URL\n#//imgops.com/%URL;types:gif,jpg,png\n#//www.gif-explode.com/%URL;types:gif",
FappeT: {
werk: false
},
@@ -4303,7 +4304,27 @@ report:
}\n\
#captchaContainerAlt td:nth-child(2) {\n\
display: table-cell !important;\n\
-}\n",
+}\n\
+/* Archive reports */\n\
+#archive-report {\n\
+ padding: 3px;\n\
+}\n\
+#archive-report-enabled {\n\
+ vertical-align: middle;\n\
+}\n\
+#archive-report > label {\n\
+ display: block;\n\
+}\n\
+#archive-report-reason {\n\
+ display: block;\n\
+ width: 98%;\n\
+}\n\
+.archive-report-success {\n\
+ color: green;\n\
+}\n\
+.archive-report-error {\n\
+ color: red;\n\
+}",
www:
"#captcha-cnt {\n\
@@ -5257,6 +5278,10 @@ Callbacks = (function() {
if (keys == null) {
keys = this.keys;
}
+ if (node.callbacksExecuted) {
+ return;
+ }
+ node.callbacksExecuted = true;
for (i = 0, len = keys.length; i < len; i++) {
name = keys[i];
try {
@@ -5755,7 +5780,7 @@ Fetcher = (function() {
};
Fetcher.prototype.parseArchivedPost = function(data, url, archive) {
- var board, comment, greentext, i, j, key, o, post, ref, ref1, tag, text, text2, thread, val;
+ var board, comment, greentext, i, j, media_link, o, post, ref, tag, text, text2, thread, thumb_link;
if (post = g.posts[this.boardID + "." + this.postID]) {
this.insert(post);
return;
@@ -5842,21 +5867,28 @@ Fetcher = (function() {
delete o.info.uniqueID;
}
if ((ref = data.media) != null ? ref.media_filename : void 0) {
- ref1 = data.media;
- for (key in ref1) {
- val = ref1[key];
- if (/_link$/.test(key) && (val != null ? val[0] : void 0) === '/') {
- data.media[key] = url.split('/', 3).join('/') + val;
- }
+ thumb_link = data.media.thumb_link;
+ if ((thumb_link != null ? thumb_link[0] : void 0) === '/') {
+ thumb_link = url.split('/', 3).join('/') + thumb_link;
+ }
+ if (!Redirect.securityCheck(thumb_link)) {
+ thumb_link = '';
+ }
+ media_link = Redirect.to('file', {
+ boardID: this.boardID,
+ filename: data.media.media_orig
+ });
+ if (!Redirect.securityCheck(media_link)) {
+ media_link = '';
}
o.file = {
name: data.media.media_filename,
- url: data.media.media_link || data.media.remote_media_link || (this.boardID === 'f' ? location.protocol + "//" + (ImageHost.flashHost()) + "/" + this.boardID + "/" + (encodeURIComponent(data.media.media_filename)) : location.protocol + "//" + (ImageHost.host()) + "/" + this.boardID + "/" + (encodeURIComponent(data.media.media_orig))),
+ url: media_link || (this.boardID === 'f' ? location.protocol + "//" + (ImageHost.flashHost()) + "/" + this.boardID + "/" + (encodeURIComponent(E(data.media.media_filename))) : location.protocol + "//" + (ImageHost.host()) + "/" + this.boardID + "/" + data.media.media_orig),
height: data.media.media_h,
width: data.media.media_w,
MD5: data.media.media_hash,
size: $.bytesToString(data.media.media_size),
- thumbURL: data.media.thumb_link || (location.protocol + "//" + (ImageHost.thumbHost()) + "/" + this.boardID + "/" + data.media.preview_orig),
+ thumbURL: thumb_link || (location.protocol + "//" + (ImageHost.thumbHost()) + "/" + this.boardID + "/" + data.media.preview_orig),
theight: data.media.preview_h,
twidth: data.media.preview_w,
isSpoiler: data.media.spoiler === '1'
@@ -6346,6 +6378,7 @@ Post = (function() {
};
Post.prototype.addClone = function(context, contractThumb) {
+ Callbacks.Post.execute(this);
return new Post.Clone(this, context, contractThumb);
};
@@ -6828,7 +6861,7 @@ Redirect = (function() {
Redirect = {
archives: [
- { "uid": 3, "name": "4plebs", "domain": "archive.4plebs.org", "http": true, "https": true, "software": "foolfuuka", "boards": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ], "files": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ] },
+ { "uid": 3, "name": "4plebs", "domain": "archive.4plebs.org", "http": true, "https": true, "software": "foolfuuka", "boards": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ], "files": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ], "reports": true },
{ "uid": 4, "name": "Nyafuu Archive", "domain": "archive.nyafuu.org", "http": false, "https": true, "software": "foolfuuka", "boards": [ "asp", "c", "e", "n", "news", "out", "p", "toy", "vp", "w", "wg", "wsr" ], "files": [ "asp", "c", "e", "n", "news", "out", "p", "toy", "vp", "w", "wg", "wsr" ] },
{ "uid": 8, "name": "Rebecca Black Tech", "domain": "archive.rebeccablacktech.com", "http": false, "https": true, "software": "fuuka", "boards": [ "cgl", "g", "mu" ], "files": [ "cgl", "g", "mu" ] },
{ "uid": 10, "name": "warosu", "domain": "warosu.org", "http": false, "https": true, "software": "fuuka", "boards": [ "3", "biz", "cgl", "ck", "diy", "fa", "g", "ic", "jp", "lit", "sci", "tg", "vr" ], "files": [ "3", "biz", "cgl", "ck", "diy", "fa", "g", "ic", "jp", "lit", "sci", "tg", "vr" ] },
@@ -6839,7 +6872,8 @@ Redirect = (function() {
{ "uid": 28, "name": "bstats", "domain": "archive.b-stats.org", "http": false, "https": true, "software": "foolfuuka", "boards": [ "f", "cm", "hm", "lgbt", "news", "qst", "trash", "y" ], "files": [] },
{ "uid": 29, "name": "Archived.Moe", "domain": "archived.moe", "http": true, "https": false, "software": "foolfuuka", "boards": [ "3", "a", "aco", "adv", "an", "asp", "b", "biz", "c", "cgl", "ck", "cm", "co", "d", "diy", "e", "f", "fa", "fit", "g", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "int", "jp", "k", "lgbt", "lit", "m", "mlp", "mu", "n", "news", "o", "out", "p", "po", "pol", "qa", "qst", "r", "r9k", "s", "s4s", "sci", "soc", "sp", "t", "tg", "toy", "trash", "trv", "tv", "u", "v", "vg", "vip", "vp", "vr", "w", "wg", "wsg", "wsr", "x", "y" ], "files": [ "gd", "po", "qst", "vip" ], "search": [ "aco", "adv", "an", "asp", "b", "c", "cgl", "ck", "cm", "con", "d", "diy", "e", "f", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "lgbt", "lit", "n", "news", "o", "out", "p", "po", "q", "qa", "qst", "r", "s", "soc", "trv", "u", "vip", "w", "wg", "wsg", "wsr", "x", "y" ] },
{ "uid": 30, "name": "TheBArchive.com", "domain": "thebarchive.com", "http": true, "https": false, "software": "foolfuuka", "boards": [ "b" ], "files": [ "b" ] },
- { "uid": 31, "name": "Archive Of Sins", "domain": "archiveofsins.com", "http": true, "https": false, "software": "foolfuuka", "boards": [ "h", "hc", "hm", "r", "s", "soc" ], "files": [ "h", "hc", "hm", "r", "s", "soc" ] }
+ { "uid": 31, "name": "Archive Of Sins", "domain": "archiveofsins.com", "http": true, "https": false, "software": "foolfuuka", "boards": [ "h", "hc", "hm", "r", "s", "soc" ], "files": [ "h", "hc", "hm", "r", "s", "soc" ] },
+ { "uid": 32, "name": "4tan", "domain": "boards.4tan.org", "http": false, "https": true, "software": "foolfuuka", "boards": [ "3", "a", "aco", "adv", "an", "asp", "b", "biz", "c", "cgl", "ck", "cm", "co", "d", "diy", "e", "f", "fa", "fit", "g", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "int", "jp", "k", "lgbt", "lit", "m", "mlp", "mu", "n", "news", "o", "out", "p", "po", "pol", "qa", "qst", "r", "r9k", "s", "s4s", "sci", "soc", "sp", "t", "tg", "toy", "trash", "trv", "tv", "u", "v", "vg", "vip", "vp", "vr", "w", "wg", "wsg", "wsr", "x", "y" ], "files": [], "search": [], "reports": true }
],
init: function() {
var now, ref;
@@ -7030,6 +7064,9 @@ Redirect = (function() {
file: function(archive, arg) {
var boardID, filename;
boardID = arg.boardID, filename = arg.filename;
+ if (boardID === 'f') {
+ filename = encodeURIComponent(Build.unescape(decodeURIComponent(filename)));
+ }
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + boardID + "/full_image/" + filename;
},
board: function(archive, arg) {
@@ -7058,6 +7095,19 @@ Redirect = (function() {
path = archive.software === 'foolfuuka' ? boardID + "/search/" + type + "/" + value + "/" : type === 'image' ? boardID + "/image/" + value : boardID + "/?task=search2&search_" + type + "=" + value;
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
},
+ report: function(boardID) {
+ var archive, boards, domain, https, j, len, name, ref, reports, software, urls;
+ urls = [];
+ ref = Conf['archives'];
+ for (j = 0, len = ref.length; j < len; j++) {
+ archive = ref[j];
+ software = archive.software, https = archive.https, reports = archive.reports, boards = archive.boards, name = archive.name, domain = archive.domain;
+ if (software === 'foolfuuka' && https && reports && boards instanceof Array && indexOf.call(boards, boardID) >= 0) {
+ urls.push([name, "https://" + domain + "/_/api/chan/offsite_report/"]);
+ }
+ }
+ return urls;
+ },
securityCheck: function(url) {
return /^https:\/\//.test(url) || location.protocol === 'http:' || Conf['Exempt Archives from Encryption'];
},
@@ -11020,7 +11070,7 @@ Settings = (function() {
}
},
upgrade: function(data, version) {
- var addCSS, addSauces, boardID, changes, compareString, j, k, key, len, len1, name, record, ref, ref1, ref2, ref3, ref4, ref5, ref6, rice, set, setD, type, uids, value;
+ var addCSS, addSauces, boardID, changes, compareString, j, k, key, known, len, len1, name, record, ref, ref1, ref2, ref3, ref4, ref5, ref6, rice, set, setD, type, uids, value;
changes = {};
set = function(key, value) {
return data[key] = changes[key] = value;
@@ -11207,6 +11257,14 @@ Settings = (function() {
if (compareString < '00001.00013.00001.00002') {
addSauces(['#//www.bing.com/images/search?q=imgurl:%IMG&view=detailv2&iss=sbi#enterInsights']);
}
+ if (compareString < '00001.00013.00005.00000') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/^(#?\s*)http:\/\/regex\.info\/exif\.cgi/mg, '$1http://exif.regex.info/exif.cgi'));
+ if ((known = Config['sauces'].match(/# Known filename formats:(?:\n.+)*/))) {
+ set('sauces', data['sauces'] + '\n\n' + known[0]);
+ }
+ }
+ }
return changes;
},
loadSettings: function(data, cb) {
@@ -11267,7 +11325,7 @@ Settings = (function() {
sauce: function(section) {
var ta;
$.extend(section, {
- innerHTML: "
Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
These parameters will be replaced by their corresponding values:%TURL: Thumbnail URL.%URL: Full image URL.%IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.%MD5: MD5 hash in base64.%sMD5: MD5 hash in base64 using - and _.%hMD5: MD5 hash in hexadecimal.%name: Original file name.%board: Current board.%%, %semi: Literal % and ;.
"
+ innerHTML: "Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
You can specify a regular expression the filename must match by appending ;regexp:[regular expression].
These parameters will be replaced by their corresponding values in the URL and displayed text:%TURL: Thumbnail URL.%URL: Full image URL.%IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.%MD5: MD5 hash in base64.%sMD5: MD5 hash in base64 using - and _.%hMD5: MD5 hash in hexadecimal.%name: Original file name.%board: Current board.%$0: The matched regular expression.%$1, %$2, and so on: Subexpressions within the matched regular expression.%%, %semi: Literal % and ;.
"
});
$('.warning', section).hidden = Conf['Sauce'];
ta = $('textarea', section);
@@ -13683,7 +13741,7 @@ Sauce = (function() {
Sauce = {
init: function() {
- var err, j, len, link, links, ref, ref1;
+ var j, len, link, linkData, links, ref, ref1;
if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Sauce'])) {
return;
}
@@ -13691,12 +13749,8 @@ Sauce = (function() {
ref1 = Conf['sauces'].split('\n');
for (j = 0, len = ref1.length; j < len; j++) {
link = ref1[j];
- try {
- if (link[0] !== '#') {
- links.push(link.trim());
- }
- } catch (_error) {
- err = _error;
+ if (link[0] !== '#' && (linkData = this.parseLink(link))) {
+ links.push(linkData);
}
}
if (!links.length) {
@@ -13712,13 +13766,13 @@ Sauce = (function() {
cb: this.node
});
},
- createSauceLink: function(link, post) {
- var a, ext, i, j, key, len, m, part, parts, ref, ref1, ref2, skip;
+ parseLink: function(link) {
+ var err, i, j, len, m, part, parts, ref, ref1, regexp;
if (!(link = link.trim())) {
return null;
}
parts = {};
- ref = link.split(/;(?=(?:text|boards|types|sandbox):?)/);
+ ref = link.split(/;(?=(?:text|boards|types|regexp|sandbox):?)/);
for (i = j = 0, len = ref.length; j < len; i = ++j) {
part = ref[i];
if (i === 0) {
@@ -13729,15 +13783,52 @@ Sauce = (function() {
}
}
parts['text'] || (parts['text'] = ((ref1 = parts['url'].match(/(\w+)\.\w+\//)) != null ? ref1[1] : void 0) || '?');
+ if ('regexp' in parts) {
+ try {
+ if ((regexp = parts['regexp'].match(/^\/(.*)\/(\w*)$/))) {
+ parts['regexp'] = RegExp(regexp[1], regexp[2]);
+ } else {
+ parts['regexp'] = RegExp(parts['regexp']);
+ }
+ } catch (_error) {
+ err = _error;
+ new Notice('warning', [$.tn("Invalid regexp for Sauce link:"), $.el('br'), $.tn(link), $.el('br'), $.tn(err.message)], 60);
+ return null;
+ }
+ }
+ return parts;
+ },
+ createSauceLink: function(link, post) {
+ var a, ext, j, key, len, matches, parts, ref, ref1, skip;
ext = post.file.url.match(/[^.]*$/)[0];
+ parts = {};
+ $.extend(parts, link);
+ if (!(!parts['boards'] || (ref = post.board.ID, indexOf.call(parts['boards'].split(','), ref) >= 0))) {
+ return null;
+ }
+ if (!(!parts['types'] || indexOf.call(parts['types'].split(','), ext) >= 0)) {
+ return null;
+ }
+ if (!(!parts['regexp'] || (matches = post.file.name.match(parts['regexp'])))) {
+ return null;
+ }
skip = false;
- for (key in parts) {
- parts[key] = parts[key].replace(/%(T?URL|IMG|[sh]?MD5|board|name|%|semi)/g, function(_, parameter) {
+ ref1 = ['url', 'text'];
+ for (j = 0, len = ref1.length; j < len; j++) {
+ key = ref1[j];
+ parts[key] = parts[key].replace(/%(T?URL|IMG|[sh]?MD5|board|name|%|semi|\$\d+)/g, function(_, parameter) {
var type;
- type = Sauce.formatters[parameter](post, ext);
- if (type == null) {
- skip = true;
- return '';
+ if (parameter[0] === '$') {
+ if (!matches) {
+ return parameter;
+ }
+ type = matches[parameter.slice(1)];
+ } else {
+ type = Sauce.formatters[parameter](post, ext);
+ if (type == null) {
+ skip = true;
+ return '';
+ }
}
if (key === 'url' && (parameter !== '%' && parameter !== 'semi')) {
if (/^javascript:/i.test(parts['url'])) {
@@ -13751,12 +13842,6 @@ Sauce = (function() {
if (skip) {
return null;
}
- if (!(!parts['boards'] || (ref2 = post.board.ID, indexOf.call(parts['boards'].split(','), ref2) >= 0))) {
- return null;
- }
- if (!(!parts['types'] || indexOf.call(parts['types'].split(','), ext) >= 0)) {
- return null;
- }
a = Sauce.link.cloneNode(false);
a.href = parts['url'];
a.textContent = parts['text'];
@@ -15266,26 +15351,22 @@ ReportLink = (function() {
}
a = $.el('a', {
className: 'report-link',
- href: 'javascript:;'
+ href: 'javascript:;',
+ textContent: 'Report'
});
$.on(a, 'click', ReportLink.report);
return Menu.menu.addEntry({
el: a,
order: 10,
open: function(post) {
- if (!(post.isDead || (post.thread.isDead && !post.thread.isArchived))) {
- a.textContent = 'Report';
- ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post;
- if ((Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) || d.cookie.indexOf('pass_enabled=1') >= 0) {
- ReportLink.url += '&altc=1';
- ReportLink.dims = 'width=350,height=275';
- } else {
- ReportLink.dims = 'width=400,height=550';
- }
+ ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post;
+ if ((Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) || d.cookie.indexOf('pass_enabled=1') >= 0) {
+ ReportLink.url += '&altc=1';
+ ReportLink.dims = 'width=350,height=275';
} else {
- ReportLink.url = '';
+ ReportLink.dims = 'width=400,height=550';
}
- return !!ReportLink.url;
+ return true;
}
});
},
@@ -17355,18 +17436,18 @@ Report = (function() {
},
ready: function() {
$.addStyle(CSS.report);
- if (!Conf['Use Recaptcha v1 in Reports'] && !Conf['Force Noscript Captcha'] && Main.jsEnabled) {
- return new MutationObserver(function() {
- Report.fit('iframe[src^="https://www.google.com/recaptcha/api2/frame"]');
- return Report.fit('body');
- }).observe(d.body, {
- childList: true,
- attributes: true,
- subtree: true
- });
- } else {
- return Report.fit('body');
+ if (Conf['Archive Report']) {
+ Report.archive();
}
+ new MutationObserver(function() {
+ Report.fit('iframe[src^="https://www.google.com/recaptcha/api2/frame"]');
+ return Report.fit('body');
+ }).observe(d.body, {
+ childList: true,
+ attributes: true,
+ subtree: true
+ });
+ return Report.fit('body');
},
fit: function(selector) {
var dy, el;
@@ -17377,6 +17458,111 @@ Report = (function() {
if (dy > 0) {
return window.resizeBy(0, dy);
}
+ },
+ archive: function() {
+ var enabled, fieldset, form, match, message, reason, submit, types, urls;
+ if (!(urls = Redirect.report(g.BOARD.ID)).length) {
+ return;
+ }
+ form = $('form');
+ types = $.id('reportTypes');
+ message = $('h3');
+ fieldset = $.el('fieldset', {
+ id: 'archive-report',
+ hidden: true
+ }, {
+ innerHTML: ""
+ });
+ enabled = $('#archive-report-enabled', fieldset);
+ reason = $('#archive-report-reason', fieldset);
+ submit = $('#archive-report-submit', fieldset);
+ if (form && types) {
+ fieldset.hidden = !$('[value=illegal]', types).checked;
+ $.on(types, 'change', function(e) {
+ fieldset.hidden = e.target.value !== 'illegal';
+ return Report.fit('body');
+ });
+ $.after(types, fieldset);
+ Report.fit('body');
+ $.one(form, 'submit', function(e) {
+ if (!fieldset.hidden && enabled.checked) {
+ e.preventDefault();
+ return Report.archiveSubmit(urls, reason.value, (function(_this) {
+ return function(results) {
+ _this.action = '#archiveresults=' + encodeURIComponent(JSON.stringify(results));
+ return _this.submit();
+ };
+ })(this));
+ }
+ });
+ } else if (message) {
+ enabled.checked = false;
+ fieldset.hidden = false;
+ $.on(enabled, 'change', function() {
+ return submit.hidden = !this.checked;
+ });
+ $.after(message, fieldset);
+ $.on(submit, 'click', function() {
+ return Report.archiveSubmit(urls, reason.value, Report.archiveResults);
+ });
+ }
+ if ((match = location.hash.match(/^#archiveresults=(.*)$/))) {
+ try {
+ return Report.archiveResults(JSON.parse(decodeURIComponent(match[1])));
+ } catch (_error) {}
+ }
+ },
+ archiveSubmit: function(urls, reason, cb) {
+ var fn, form, i, len, name, ref, results, url;
+ form = $.formData({
+ board: g.BOARD.ID,
+ num: Report.postID,
+ reason: reason
+ });
+ results = [];
+ fn = function(name, url) {
+ return $.ajax(url, {
+ responseType: 'json',
+ onloadend: function() {
+ results.push([
+ name, this.response || {
+ error: ''
+ }
+ ]);
+ if (results.length === urls.length) {
+ return cb(results);
+ }
+ }
+ }, {
+ form: form
+ });
+ };
+ for (i = 0, len = urls.length; i < len; i++) {
+ ref = urls[i], name = ref[0], url = ref[1];
+ fn(name, url);
+ }
+ },
+ archiveResults: function(results) {
+ var fieldset, i, len, line, name, ref, response;
+ fieldset = $.id('archive-report');
+ for (i = 0, len = results.length; i < len; i++) {
+ ref = results[i], name = ref[0], response = ref[1];
+ line = $.el('h3', {
+ className: 'archive-report-response'
+ });
+ if ('success' in response) {
+ $.addClass(line, 'archive-report-success');
+ line.textContent = name + ": " + response.success;
+ } else {
+ $.addClass(line, 'archive-report-error');
+ line.textContent = name + ": " + (response.error || 'Error reporting post.');
+ }
+ if (fieldset) {
+ $.before(fieldset, line);
+ } else {
+ $.add(d.body, line);
+ }
+ }
}
};
@@ -18931,8 +19117,9 @@ ThreadWatcher = (function() {
ref = [thread.OP].concat(slice.call(thread.OP.clones));
for (i = 0, len = ref.length; i < len; i++) {
post = ref[i];
- toggler = $('.watch-thread-link', post.nodes.info);
- ThreadWatcher.setToggler(toggler, isWatched);
+ if ((toggler = $('.watch-thread-link', post.nodes.info))) {
+ ThreadWatcher.setToggler(toggler, isWatched);
+ }
}
}
if (thread.catalogView) {
diff --git a/builds/4chan-X-noupdate.crx b/builds/4chan-X-noupdate.crx
index 75e3e6b4c..5483da5e3 100644
Binary files a/builds/4chan-X-noupdate.crx and b/builds/4chan-X-noupdate.crx differ
diff --git a/builds/4chan-X-noupdate.user.js b/builds/4chan-X-noupdate.user.js
index c0e9ca8ee..0437c68b9 100644
--- a/builds/4chan-X-noupdate.user.js
+++ b/builds/4chan-X-noupdate.user.js
@@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X
-// @version 1.13.4.1
+// @version 1.13.5.0
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@@ -150,7 +150,7 @@ docSet = function() {
};
g = {
- VERSION: '1.13.4.1',
+ VERSION: '1.13.5.0',
NAMESPACE: '4chan X.',
boards: {}
};
@@ -202,6 +202,7 @@ Config = (function() {
'Announcement Hiding': [true, 'Add button to hide 4chan announcements.'],
'Desktop Notifications': [true, 'Enables desktop notifications across various 4chan X features.'],
'404 Redirect': [true, 'Redirect dead threads and images to the archives.'],
+ 'Archive Report': [true, 'Enable reporting posts to supported archives.'],
'Exempt Archives from Encryption': [true, 'Permit loading content from, and warningless redirects to, HTTP-only archives from HTTPS pages.'],
'Keybinds': [true, 'Bind actions to keyboard shortcuts.'],
'Time Formatting': [true, 'Localize and format timestamps.'],
@@ -379,7 +380,7 @@ Config = (function() {
filesize: '',
MD5: ''
},
- sauces: "# Reverse image search:\nhttps://www.google.com/searchbyimage?image_url=%IMG&safe=off\nhttps://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//tineye.com/search?url=%IMG\n#//www.bing.com/images/search?q=imgurl:%IMG&view=detailv2&iss=sbi#enterInsights\n\n# Specialized reverse image search:\n//iqdb.org/?url=%IMG\nhttps://whatanime.ga/?auto&url=%IMG;text:wait\n#//3d.iqdb.org/?url=%IMG\n#//saucenao.com/search.php?url=%IMG\n\n# \"View Same\" in archives:\nhttp://eye.swfchan.com/search/?q=%name;types:swf\n#https://desuarchive.org/_/search/image/%sMD5/\n#https://archive.4plebs.org/_/search/image/%sMD5/\n#https://boards.fireden.net/_/search/image/%sMD5/\n#https://foolz.fireden.net/_/search/image/%sMD5/\n\n# Other tools:\n#http://regex.info/exif.cgi?imgurl=%URL\n#//imgops.com/%URL;types:gif,jpg,png\n#//www.gif-explode.com/%URL;types:gif",
+ sauces: "# Known filename formats:\nhttp://www.pixiv.net/member_illust.php?mode=medium&illust_id=%$1;regexp:/^(\\d+)_p\\d+/\n//%$1.deviantart.com/gallery/#/d%$2;regexp:/^\\w+_by_(\\w+)-d([\\da-z]+)/\n//imgur.com/%$1;regexp:/^(?![a-zA-Z][a-z]{6})(?![A-Z]{7})(?!\\d{7})([\\da-zA-Z]{7})(?: \\(\\d+\\))?\\.\\w+$/\nhttp://flickr.com/photo.gne?id=%$1;regexp:/^(\\d+)_[\\da-f]{10}(?:_\\w)*\\b/\nhttps://www.facebook.com/photo.php?fbid=%$1;regexp:/^\\d+_(\\d+)_\\d+_[no]\\b/\n\n# Reverse image search:\nhttps://www.google.com/searchbyimage?image_url=%IMG&safe=off\nhttps://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//tineye.com/search?url=%IMG\n#//www.bing.com/images/search?q=imgurl:%IMG&view=detailv2&iss=sbi#enterInsights\n\n# Specialized reverse image search:\n//iqdb.org/?url=%IMG\nhttps://whatanime.ga/?auto&url=%IMG;text:wait\n#//3d.iqdb.org/?url=%IMG\n#//saucenao.com/search.php?url=%IMG\n\n# \"View Same\" in archives:\nhttp://eye.swfchan.com/search/?q=%name;types:swf\n#https://desuarchive.org/_/search/image/%sMD5/\n#https://archive.4plebs.org/_/search/image/%sMD5/\n#https://boards.fireden.net/_/search/image/%sMD5/\n#https://foolz.fireden.net/_/search/image/%sMD5/\n\n# Other tools:\n#http://exif.regex.info/exif.cgi?imgurl=%URL\n#//imgops.com/%URL;types:gif,jpg,png\n#//www.gif-explode.com/%URL;types:gif",
FappeT: {
werk: false
},
@@ -4303,7 +4304,27 @@ report:
}\n\
#captchaContainerAlt td:nth-child(2) {\n\
display: table-cell !important;\n\
-}\n",
+}\n\
+/* Archive reports */\n\
+#archive-report {\n\
+ padding: 3px;\n\
+}\n\
+#archive-report-enabled {\n\
+ vertical-align: middle;\n\
+}\n\
+#archive-report > label {\n\
+ display: block;\n\
+}\n\
+#archive-report-reason {\n\
+ display: block;\n\
+ width: 98%;\n\
+}\n\
+.archive-report-success {\n\
+ color: green;\n\
+}\n\
+.archive-report-error {\n\
+ color: red;\n\
+}",
www:
"#captcha-cnt {\n\
@@ -5257,6 +5278,10 @@ Callbacks = (function() {
if (keys == null) {
keys = this.keys;
}
+ if (node.callbacksExecuted) {
+ return;
+ }
+ node.callbacksExecuted = true;
for (i = 0, len = keys.length; i < len; i++) {
name = keys[i];
try {
@@ -5755,7 +5780,7 @@ Fetcher = (function() {
};
Fetcher.prototype.parseArchivedPost = function(data, url, archive) {
- var board, comment, greentext, i, j, key, o, post, ref, ref1, tag, text, text2, thread, val;
+ var board, comment, greentext, i, j, media_link, o, post, ref, tag, text, text2, thread, thumb_link;
if (post = g.posts[this.boardID + "." + this.postID]) {
this.insert(post);
return;
@@ -5842,21 +5867,28 @@ Fetcher = (function() {
delete o.info.uniqueID;
}
if ((ref = data.media) != null ? ref.media_filename : void 0) {
- ref1 = data.media;
- for (key in ref1) {
- val = ref1[key];
- if (/_link$/.test(key) && (val != null ? val[0] : void 0) === '/') {
- data.media[key] = url.split('/', 3).join('/') + val;
- }
+ thumb_link = data.media.thumb_link;
+ if ((thumb_link != null ? thumb_link[0] : void 0) === '/') {
+ thumb_link = url.split('/', 3).join('/') + thumb_link;
+ }
+ if (!Redirect.securityCheck(thumb_link)) {
+ thumb_link = '';
+ }
+ media_link = Redirect.to('file', {
+ boardID: this.boardID,
+ filename: data.media.media_orig
+ });
+ if (!Redirect.securityCheck(media_link)) {
+ media_link = '';
}
o.file = {
name: data.media.media_filename,
- url: data.media.media_link || data.media.remote_media_link || (this.boardID === 'f' ? location.protocol + "//" + (ImageHost.flashHost()) + "/" + this.boardID + "/" + (encodeURIComponent(data.media.media_filename)) : location.protocol + "//" + (ImageHost.host()) + "/" + this.boardID + "/" + (encodeURIComponent(data.media.media_orig))),
+ url: media_link || (this.boardID === 'f' ? location.protocol + "//" + (ImageHost.flashHost()) + "/" + this.boardID + "/" + (encodeURIComponent(E(data.media.media_filename))) : location.protocol + "//" + (ImageHost.host()) + "/" + this.boardID + "/" + data.media.media_orig),
height: data.media.media_h,
width: data.media.media_w,
MD5: data.media.media_hash,
size: $.bytesToString(data.media.media_size),
- thumbURL: data.media.thumb_link || (location.protocol + "//" + (ImageHost.thumbHost()) + "/" + this.boardID + "/" + data.media.preview_orig),
+ thumbURL: thumb_link || (location.protocol + "//" + (ImageHost.thumbHost()) + "/" + this.boardID + "/" + data.media.preview_orig),
theight: data.media.preview_h,
twidth: data.media.preview_w,
isSpoiler: data.media.spoiler === '1'
@@ -6346,6 +6378,7 @@ Post = (function() {
};
Post.prototype.addClone = function(context, contractThumb) {
+ Callbacks.Post.execute(this);
return new Post.Clone(this, context, contractThumb);
};
@@ -6828,7 +6861,7 @@ Redirect = (function() {
Redirect = {
archives: [
- { "uid": 3, "name": "4plebs", "domain": "archive.4plebs.org", "http": true, "https": true, "software": "foolfuuka", "boards": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ], "files": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ] },
+ { "uid": 3, "name": "4plebs", "domain": "archive.4plebs.org", "http": true, "https": true, "software": "foolfuuka", "boards": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ], "files": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ], "reports": true },
{ "uid": 4, "name": "Nyafuu Archive", "domain": "archive.nyafuu.org", "http": false, "https": true, "software": "foolfuuka", "boards": [ "asp", "c", "e", "n", "news", "out", "p", "toy", "vp", "w", "wg", "wsr" ], "files": [ "asp", "c", "e", "n", "news", "out", "p", "toy", "vp", "w", "wg", "wsr" ] },
{ "uid": 8, "name": "Rebecca Black Tech", "domain": "archive.rebeccablacktech.com", "http": false, "https": true, "software": "fuuka", "boards": [ "cgl", "g", "mu" ], "files": [ "cgl", "g", "mu" ] },
{ "uid": 10, "name": "warosu", "domain": "warosu.org", "http": false, "https": true, "software": "fuuka", "boards": [ "3", "biz", "cgl", "ck", "diy", "fa", "g", "ic", "jp", "lit", "sci", "tg", "vr" ], "files": [ "3", "biz", "cgl", "ck", "diy", "fa", "g", "ic", "jp", "lit", "sci", "tg", "vr" ] },
@@ -6839,7 +6872,8 @@ Redirect = (function() {
{ "uid": 28, "name": "bstats", "domain": "archive.b-stats.org", "http": false, "https": true, "software": "foolfuuka", "boards": [ "f", "cm", "hm", "lgbt", "news", "qst", "trash", "y" ], "files": [] },
{ "uid": 29, "name": "Archived.Moe", "domain": "archived.moe", "http": true, "https": false, "software": "foolfuuka", "boards": [ "3", "a", "aco", "adv", "an", "asp", "b", "biz", "c", "cgl", "ck", "cm", "co", "d", "diy", "e", "f", "fa", "fit", "g", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "int", "jp", "k", "lgbt", "lit", "m", "mlp", "mu", "n", "news", "o", "out", "p", "po", "pol", "qa", "qst", "r", "r9k", "s", "s4s", "sci", "soc", "sp", "t", "tg", "toy", "trash", "trv", "tv", "u", "v", "vg", "vip", "vp", "vr", "w", "wg", "wsg", "wsr", "x", "y" ], "files": [ "gd", "po", "qst", "vip" ], "search": [ "aco", "adv", "an", "asp", "b", "c", "cgl", "ck", "cm", "con", "d", "diy", "e", "f", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "lgbt", "lit", "n", "news", "o", "out", "p", "po", "q", "qa", "qst", "r", "s", "soc", "trv", "u", "vip", "w", "wg", "wsg", "wsr", "x", "y" ] },
{ "uid": 30, "name": "TheBArchive.com", "domain": "thebarchive.com", "http": true, "https": false, "software": "foolfuuka", "boards": [ "b" ], "files": [ "b" ] },
- { "uid": 31, "name": "Archive Of Sins", "domain": "archiveofsins.com", "http": true, "https": false, "software": "foolfuuka", "boards": [ "h", "hc", "hm", "r", "s", "soc" ], "files": [ "h", "hc", "hm", "r", "s", "soc" ] }
+ { "uid": 31, "name": "Archive Of Sins", "domain": "archiveofsins.com", "http": true, "https": false, "software": "foolfuuka", "boards": [ "h", "hc", "hm", "r", "s", "soc" ], "files": [ "h", "hc", "hm", "r", "s", "soc" ] },
+ { "uid": 32, "name": "4tan", "domain": "boards.4tan.org", "http": false, "https": true, "software": "foolfuuka", "boards": [ "3", "a", "aco", "adv", "an", "asp", "b", "biz", "c", "cgl", "ck", "cm", "co", "d", "diy", "e", "f", "fa", "fit", "g", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "int", "jp", "k", "lgbt", "lit", "m", "mlp", "mu", "n", "news", "o", "out", "p", "po", "pol", "qa", "qst", "r", "r9k", "s", "s4s", "sci", "soc", "sp", "t", "tg", "toy", "trash", "trv", "tv", "u", "v", "vg", "vip", "vp", "vr", "w", "wg", "wsg", "wsr", "x", "y" ], "files": [], "search": [], "reports": true }
],
init: function() {
var now, ref;
@@ -7030,6 +7064,9 @@ Redirect = (function() {
file: function(archive, arg) {
var boardID, filename;
boardID = arg.boardID, filename = arg.filename;
+ if (boardID === 'f') {
+ filename = encodeURIComponent(Build.unescape(decodeURIComponent(filename)));
+ }
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + boardID + "/full_image/" + filename;
},
board: function(archive, arg) {
@@ -7058,6 +7095,19 @@ Redirect = (function() {
path = archive.software === 'foolfuuka' ? boardID + "/search/" + type + "/" + value + "/" : type === 'image' ? boardID + "/image/" + value : boardID + "/?task=search2&search_" + type + "=" + value;
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
},
+ report: function(boardID) {
+ var archive, boards, domain, https, j, len, name, ref, reports, software, urls;
+ urls = [];
+ ref = Conf['archives'];
+ for (j = 0, len = ref.length; j < len; j++) {
+ archive = ref[j];
+ software = archive.software, https = archive.https, reports = archive.reports, boards = archive.boards, name = archive.name, domain = archive.domain;
+ if (software === 'foolfuuka' && https && reports && boards instanceof Array && indexOf.call(boards, boardID) >= 0) {
+ urls.push([name, "https://" + domain + "/_/api/chan/offsite_report/"]);
+ }
+ }
+ return urls;
+ },
securityCheck: function(url) {
return /^https:\/\//.test(url) || location.protocol === 'http:' || Conf['Exempt Archives from Encryption'];
},
@@ -11020,7 +11070,7 @@ Settings = (function() {
}
},
upgrade: function(data, version) {
- var addCSS, addSauces, boardID, changes, compareString, j, k, key, len, len1, name, record, ref, ref1, ref2, ref3, ref4, ref5, ref6, rice, set, setD, type, uids, value;
+ var addCSS, addSauces, boardID, changes, compareString, j, k, key, known, len, len1, name, record, ref, ref1, ref2, ref3, ref4, ref5, ref6, rice, set, setD, type, uids, value;
changes = {};
set = function(key, value) {
return data[key] = changes[key] = value;
@@ -11207,6 +11257,14 @@ Settings = (function() {
if (compareString < '00001.00013.00001.00002') {
addSauces(['#//www.bing.com/images/search?q=imgurl:%IMG&view=detailv2&iss=sbi#enterInsights']);
}
+ if (compareString < '00001.00013.00005.00000') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/^(#?\s*)http:\/\/regex\.info\/exif\.cgi/mg, '$1http://exif.regex.info/exif.cgi'));
+ if ((known = Config['sauces'].match(/# Known filename formats:(?:\n.+)*/))) {
+ set('sauces', data['sauces'] + '\n\n' + known[0]);
+ }
+ }
+ }
return changes;
},
loadSettings: function(data, cb) {
@@ -11267,7 +11325,7 @@ Settings = (function() {
sauce: function(section) {
var ta;
$.extend(section, {
- innerHTML: "Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
These parameters will be replaced by their corresponding values:%TURL: Thumbnail URL.%URL: Full image URL.%IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.%MD5: MD5 hash in base64.%sMD5: MD5 hash in base64 using - and _.%hMD5: MD5 hash in hexadecimal.%name: Original file name.%board: Current board.%%, %semi: Literal % and ;.
"
+ innerHTML: "Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
You can specify a regular expression the filename must match by appending ;regexp:[regular expression].
These parameters will be replaced by their corresponding values in the URL and displayed text:%TURL: Thumbnail URL.%URL: Full image URL.%IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.%MD5: MD5 hash in base64.%sMD5: MD5 hash in base64 using - and _.%hMD5: MD5 hash in hexadecimal.%name: Original file name.%board: Current board.%$0: The matched regular expression.%$1, %$2, and so on: Subexpressions within the matched regular expression.%%, %semi: Literal % and ;.
"
});
$('.warning', section).hidden = Conf['Sauce'];
ta = $('textarea', section);
@@ -13683,7 +13741,7 @@ Sauce = (function() {
Sauce = {
init: function() {
- var err, j, len, link, links, ref, ref1;
+ var j, len, link, linkData, links, ref, ref1;
if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Sauce'])) {
return;
}
@@ -13691,12 +13749,8 @@ Sauce = (function() {
ref1 = Conf['sauces'].split('\n');
for (j = 0, len = ref1.length; j < len; j++) {
link = ref1[j];
- try {
- if (link[0] !== '#') {
- links.push(link.trim());
- }
- } catch (_error) {
- err = _error;
+ if (link[0] !== '#' && (linkData = this.parseLink(link))) {
+ links.push(linkData);
}
}
if (!links.length) {
@@ -13712,13 +13766,13 @@ Sauce = (function() {
cb: this.node
});
},
- createSauceLink: function(link, post) {
- var a, ext, i, j, key, len, m, part, parts, ref, ref1, ref2, skip;
+ parseLink: function(link) {
+ var err, i, j, len, m, part, parts, ref, ref1, regexp;
if (!(link = link.trim())) {
return null;
}
parts = {};
- ref = link.split(/;(?=(?:text|boards|types|sandbox):?)/);
+ ref = link.split(/;(?=(?:text|boards|types|regexp|sandbox):?)/);
for (i = j = 0, len = ref.length; j < len; i = ++j) {
part = ref[i];
if (i === 0) {
@@ -13729,15 +13783,52 @@ Sauce = (function() {
}
}
parts['text'] || (parts['text'] = ((ref1 = parts['url'].match(/(\w+)\.\w+\//)) != null ? ref1[1] : void 0) || '?');
+ if ('regexp' in parts) {
+ try {
+ if ((regexp = parts['regexp'].match(/^\/(.*)\/(\w*)$/))) {
+ parts['regexp'] = RegExp(regexp[1], regexp[2]);
+ } else {
+ parts['regexp'] = RegExp(parts['regexp']);
+ }
+ } catch (_error) {
+ err = _error;
+ new Notice('warning', [$.tn("Invalid regexp for Sauce link:"), $.el('br'), $.tn(link), $.el('br'), $.tn(err.message)], 60);
+ return null;
+ }
+ }
+ return parts;
+ },
+ createSauceLink: function(link, post) {
+ var a, ext, j, key, len, matches, parts, ref, ref1, skip;
ext = post.file.url.match(/[^.]*$/)[0];
+ parts = {};
+ $.extend(parts, link);
+ if (!(!parts['boards'] || (ref = post.board.ID, indexOf.call(parts['boards'].split(','), ref) >= 0))) {
+ return null;
+ }
+ if (!(!parts['types'] || indexOf.call(parts['types'].split(','), ext) >= 0)) {
+ return null;
+ }
+ if (!(!parts['regexp'] || (matches = post.file.name.match(parts['regexp'])))) {
+ return null;
+ }
skip = false;
- for (key in parts) {
- parts[key] = parts[key].replace(/%(T?URL|IMG|[sh]?MD5|board|name|%|semi)/g, function(_, parameter) {
+ ref1 = ['url', 'text'];
+ for (j = 0, len = ref1.length; j < len; j++) {
+ key = ref1[j];
+ parts[key] = parts[key].replace(/%(T?URL|IMG|[sh]?MD5|board|name|%|semi|\$\d+)/g, function(_, parameter) {
var type;
- type = Sauce.formatters[parameter](post, ext);
- if (type == null) {
- skip = true;
- return '';
+ if (parameter[0] === '$') {
+ if (!matches) {
+ return parameter;
+ }
+ type = matches[parameter.slice(1)];
+ } else {
+ type = Sauce.formatters[parameter](post, ext);
+ if (type == null) {
+ skip = true;
+ return '';
+ }
}
if (key === 'url' && (parameter !== '%' && parameter !== 'semi')) {
if (/^javascript:/i.test(parts['url'])) {
@@ -13751,12 +13842,6 @@ Sauce = (function() {
if (skip) {
return null;
}
- if (!(!parts['boards'] || (ref2 = post.board.ID, indexOf.call(parts['boards'].split(','), ref2) >= 0))) {
- return null;
- }
- if (!(!parts['types'] || indexOf.call(parts['types'].split(','), ext) >= 0)) {
- return null;
- }
a = Sauce.link.cloneNode(false);
a.href = parts['url'];
a.textContent = parts['text'];
@@ -15266,26 +15351,22 @@ ReportLink = (function() {
}
a = $.el('a', {
className: 'report-link',
- href: 'javascript:;'
+ href: 'javascript:;',
+ textContent: 'Report'
});
$.on(a, 'click', ReportLink.report);
return Menu.menu.addEntry({
el: a,
order: 10,
open: function(post) {
- if (!(post.isDead || (post.thread.isDead && !post.thread.isArchived))) {
- a.textContent = 'Report';
- ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post;
- if ((Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) || d.cookie.indexOf('pass_enabled=1') >= 0) {
- ReportLink.url += '&altc=1';
- ReportLink.dims = 'width=350,height=275';
- } else {
- ReportLink.dims = 'width=400,height=550';
- }
+ ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post;
+ if ((Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) || d.cookie.indexOf('pass_enabled=1') >= 0) {
+ ReportLink.url += '&altc=1';
+ ReportLink.dims = 'width=350,height=275';
} else {
- ReportLink.url = '';
+ ReportLink.dims = 'width=400,height=550';
}
- return !!ReportLink.url;
+ return true;
}
});
},
@@ -17355,18 +17436,18 @@ Report = (function() {
},
ready: function() {
$.addStyle(CSS.report);
- if (!Conf['Use Recaptcha v1 in Reports'] && !Conf['Force Noscript Captcha'] && Main.jsEnabled) {
- return new MutationObserver(function() {
- Report.fit('iframe[src^="https://www.google.com/recaptcha/api2/frame"]');
- return Report.fit('body');
- }).observe(d.body, {
- childList: true,
- attributes: true,
- subtree: true
- });
- } else {
- return Report.fit('body');
+ if (Conf['Archive Report']) {
+ Report.archive();
}
+ new MutationObserver(function() {
+ Report.fit('iframe[src^="https://www.google.com/recaptcha/api2/frame"]');
+ return Report.fit('body');
+ }).observe(d.body, {
+ childList: true,
+ attributes: true,
+ subtree: true
+ });
+ return Report.fit('body');
},
fit: function(selector) {
var dy, el;
@@ -17377,6 +17458,111 @@ Report = (function() {
if (dy > 0) {
return window.resizeBy(0, dy);
}
+ },
+ archive: function() {
+ var enabled, fieldset, form, match, message, reason, submit, types, urls;
+ if (!(urls = Redirect.report(g.BOARD.ID)).length) {
+ return;
+ }
+ form = $('form');
+ types = $.id('reportTypes');
+ message = $('h3');
+ fieldset = $.el('fieldset', {
+ id: 'archive-report',
+ hidden: true
+ }, {
+ innerHTML: ""
+ });
+ enabled = $('#archive-report-enabled', fieldset);
+ reason = $('#archive-report-reason', fieldset);
+ submit = $('#archive-report-submit', fieldset);
+ if (form && types) {
+ fieldset.hidden = !$('[value=illegal]', types).checked;
+ $.on(types, 'change', function(e) {
+ fieldset.hidden = e.target.value !== 'illegal';
+ return Report.fit('body');
+ });
+ $.after(types, fieldset);
+ Report.fit('body');
+ $.one(form, 'submit', function(e) {
+ if (!fieldset.hidden && enabled.checked) {
+ e.preventDefault();
+ return Report.archiveSubmit(urls, reason.value, (function(_this) {
+ return function(results) {
+ _this.action = '#archiveresults=' + encodeURIComponent(JSON.stringify(results));
+ return _this.submit();
+ };
+ })(this));
+ }
+ });
+ } else if (message) {
+ enabled.checked = false;
+ fieldset.hidden = false;
+ $.on(enabled, 'change', function() {
+ return submit.hidden = !this.checked;
+ });
+ $.after(message, fieldset);
+ $.on(submit, 'click', function() {
+ return Report.archiveSubmit(urls, reason.value, Report.archiveResults);
+ });
+ }
+ if ((match = location.hash.match(/^#archiveresults=(.*)$/))) {
+ try {
+ return Report.archiveResults(JSON.parse(decodeURIComponent(match[1])));
+ } catch (_error) {}
+ }
+ },
+ archiveSubmit: function(urls, reason, cb) {
+ var fn, form, i, len, name, ref, results, url;
+ form = $.formData({
+ board: g.BOARD.ID,
+ num: Report.postID,
+ reason: reason
+ });
+ results = [];
+ fn = function(name, url) {
+ return $.ajax(url, {
+ responseType: 'json',
+ onloadend: function() {
+ results.push([
+ name, this.response || {
+ error: ''
+ }
+ ]);
+ if (results.length === urls.length) {
+ return cb(results);
+ }
+ }
+ }, {
+ form: form
+ });
+ };
+ for (i = 0, len = urls.length; i < len; i++) {
+ ref = urls[i], name = ref[0], url = ref[1];
+ fn(name, url);
+ }
+ },
+ archiveResults: function(results) {
+ var fieldset, i, len, line, name, ref, response;
+ fieldset = $.id('archive-report');
+ for (i = 0, len = results.length; i < len; i++) {
+ ref = results[i], name = ref[0], response = ref[1];
+ line = $.el('h3', {
+ className: 'archive-report-response'
+ });
+ if ('success' in response) {
+ $.addClass(line, 'archive-report-success');
+ line.textContent = name + ": " + response.success;
+ } else {
+ $.addClass(line, 'archive-report-error');
+ line.textContent = name + ": " + (response.error || 'Error reporting post.');
+ }
+ if (fieldset) {
+ $.before(fieldset, line);
+ } else {
+ $.add(d.body, line);
+ }
+ }
}
};
@@ -18931,8 +19117,9 @@ ThreadWatcher = (function() {
ref = [thread.OP].concat(slice.call(thread.OP.clones));
for (i = 0, len = ref.length; i < len; i++) {
post = ref[i];
- toggler = $('.watch-thread-link', post.nodes.info);
- ThreadWatcher.setToggler(toggler, isWatched);
+ if ((toggler = $('.watch-thread-link', post.nodes.info))) {
+ ThreadWatcher.setToggler(toggler, isWatched);
+ }
}
}
if (thread.catalogView) {
diff --git a/builds/4chan-X.crx b/builds/4chan-X.crx
index 6909ed1f4..a7b2bb073 100644
Binary files a/builds/4chan-X.crx and b/builds/4chan-X.crx differ
diff --git a/builds/4chan-X.meta.js b/builds/4chan-X.meta.js
index d9dad62d7..7874a954c 100644
--- a/builds/4chan-X.meta.js
+++ b/builds/4chan-X.meta.js
@@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X
-// @version 1.13.4.1
+// @version 1.13.5.0
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js
index 57dcd2a2a..4322f1fa8 100644
--- a/builds/4chan-X.user.js
+++ b/builds/4chan-X.user.js
@@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X
-// @version 1.13.4.1
+// @version 1.13.5.0
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@@ -150,7 +150,7 @@ docSet = function() {
};
g = {
- VERSION: '1.13.4.1',
+ VERSION: '1.13.5.0',
NAMESPACE: '4chan X.',
boards: {}
};
@@ -202,6 +202,7 @@ Config = (function() {
'Announcement Hiding': [true, 'Add button to hide 4chan announcements.'],
'Desktop Notifications': [true, 'Enables desktop notifications across various 4chan X features.'],
'404 Redirect': [true, 'Redirect dead threads and images to the archives.'],
+ 'Archive Report': [true, 'Enable reporting posts to supported archives.'],
'Exempt Archives from Encryption': [true, 'Permit loading content from, and warningless redirects to, HTTP-only archives from HTTPS pages.'],
'Keybinds': [true, 'Bind actions to keyboard shortcuts.'],
'Time Formatting': [true, 'Localize and format timestamps.'],
@@ -379,7 +380,7 @@ Config = (function() {
filesize: '',
MD5: ''
},
- sauces: "# Reverse image search:\nhttps://www.google.com/searchbyimage?image_url=%IMG&safe=off\nhttps://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//tineye.com/search?url=%IMG\n#//www.bing.com/images/search?q=imgurl:%IMG&view=detailv2&iss=sbi#enterInsights\n\n# Specialized reverse image search:\n//iqdb.org/?url=%IMG\nhttps://whatanime.ga/?auto&url=%IMG;text:wait\n#//3d.iqdb.org/?url=%IMG\n#//saucenao.com/search.php?url=%IMG\n\n# \"View Same\" in archives:\nhttp://eye.swfchan.com/search/?q=%name;types:swf\n#https://desuarchive.org/_/search/image/%sMD5/\n#https://archive.4plebs.org/_/search/image/%sMD5/\n#https://boards.fireden.net/_/search/image/%sMD5/\n#https://foolz.fireden.net/_/search/image/%sMD5/\n\n# Other tools:\n#http://regex.info/exif.cgi?imgurl=%URL\n#//imgops.com/%URL;types:gif,jpg,png\n#//www.gif-explode.com/%URL;types:gif",
+ sauces: "# Known filename formats:\nhttp://www.pixiv.net/member_illust.php?mode=medium&illust_id=%$1;regexp:/^(\\d+)_p\\d+/\n//%$1.deviantart.com/gallery/#/d%$2;regexp:/^\\w+_by_(\\w+)-d([\\da-z]+)/\n//imgur.com/%$1;regexp:/^(?![a-zA-Z][a-z]{6})(?![A-Z]{7})(?!\\d{7})([\\da-zA-Z]{7})(?: \\(\\d+\\))?\\.\\w+$/\nhttp://flickr.com/photo.gne?id=%$1;regexp:/^(\\d+)_[\\da-f]{10}(?:_\\w)*\\b/\nhttps://www.facebook.com/photo.php?fbid=%$1;regexp:/^\\d+_(\\d+)_\\d+_[no]\\b/\n\n# Reverse image search:\nhttps://www.google.com/searchbyimage?image_url=%IMG&safe=off\nhttps://www.yandex.com/images/search?rpt=imageview&img_url=%IMG\n#//tineye.com/search?url=%IMG\n#//www.bing.com/images/search?q=imgurl:%IMG&view=detailv2&iss=sbi#enterInsights\n\n# Specialized reverse image search:\n//iqdb.org/?url=%IMG\nhttps://whatanime.ga/?auto&url=%IMG;text:wait\n#//3d.iqdb.org/?url=%IMG\n#//saucenao.com/search.php?url=%IMG\n\n# \"View Same\" in archives:\nhttp://eye.swfchan.com/search/?q=%name;types:swf\n#https://desuarchive.org/_/search/image/%sMD5/\n#https://archive.4plebs.org/_/search/image/%sMD5/\n#https://boards.fireden.net/_/search/image/%sMD5/\n#https://foolz.fireden.net/_/search/image/%sMD5/\n\n# Other tools:\n#http://exif.regex.info/exif.cgi?imgurl=%URL\n#//imgops.com/%URL;types:gif,jpg,png\n#//www.gif-explode.com/%URL;types:gif",
FappeT: {
werk: false
},
@@ -4303,7 +4304,27 @@ report:
}\n\
#captchaContainerAlt td:nth-child(2) {\n\
display: table-cell !important;\n\
-}\n",
+}\n\
+/* Archive reports */\n\
+#archive-report {\n\
+ padding: 3px;\n\
+}\n\
+#archive-report-enabled {\n\
+ vertical-align: middle;\n\
+}\n\
+#archive-report > label {\n\
+ display: block;\n\
+}\n\
+#archive-report-reason {\n\
+ display: block;\n\
+ width: 98%;\n\
+}\n\
+.archive-report-success {\n\
+ color: green;\n\
+}\n\
+.archive-report-error {\n\
+ color: red;\n\
+}",
www:
"#captcha-cnt {\n\
@@ -5257,6 +5278,10 @@ Callbacks = (function() {
if (keys == null) {
keys = this.keys;
}
+ if (node.callbacksExecuted) {
+ return;
+ }
+ node.callbacksExecuted = true;
for (i = 0, len = keys.length; i < len; i++) {
name = keys[i];
try {
@@ -5755,7 +5780,7 @@ Fetcher = (function() {
};
Fetcher.prototype.parseArchivedPost = function(data, url, archive) {
- var board, comment, greentext, i, j, key, o, post, ref, ref1, tag, text, text2, thread, val;
+ var board, comment, greentext, i, j, media_link, o, post, ref, tag, text, text2, thread, thumb_link;
if (post = g.posts[this.boardID + "." + this.postID]) {
this.insert(post);
return;
@@ -5842,21 +5867,28 @@ Fetcher = (function() {
delete o.info.uniqueID;
}
if ((ref = data.media) != null ? ref.media_filename : void 0) {
- ref1 = data.media;
- for (key in ref1) {
- val = ref1[key];
- if (/_link$/.test(key) && (val != null ? val[0] : void 0) === '/') {
- data.media[key] = url.split('/', 3).join('/') + val;
- }
+ thumb_link = data.media.thumb_link;
+ if ((thumb_link != null ? thumb_link[0] : void 0) === '/') {
+ thumb_link = url.split('/', 3).join('/') + thumb_link;
+ }
+ if (!Redirect.securityCheck(thumb_link)) {
+ thumb_link = '';
+ }
+ media_link = Redirect.to('file', {
+ boardID: this.boardID,
+ filename: data.media.media_orig
+ });
+ if (!Redirect.securityCheck(media_link)) {
+ media_link = '';
}
o.file = {
name: data.media.media_filename,
- url: data.media.media_link || data.media.remote_media_link || (this.boardID === 'f' ? location.protocol + "//" + (ImageHost.flashHost()) + "/" + this.boardID + "/" + (encodeURIComponent(data.media.media_filename)) : location.protocol + "//" + (ImageHost.host()) + "/" + this.boardID + "/" + (encodeURIComponent(data.media.media_orig))),
+ url: media_link || (this.boardID === 'f' ? location.protocol + "//" + (ImageHost.flashHost()) + "/" + this.boardID + "/" + (encodeURIComponent(E(data.media.media_filename))) : location.protocol + "//" + (ImageHost.host()) + "/" + this.boardID + "/" + data.media.media_orig),
height: data.media.media_h,
width: data.media.media_w,
MD5: data.media.media_hash,
size: $.bytesToString(data.media.media_size),
- thumbURL: data.media.thumb_link || (location.protocol + "//" + (ImageHost.thumbHost()) + "/" + this.boardID + "/" + data.media.preview_orig),
+ thumbURL: thumb_link || (location.protocol + "//" + (ImageHost.thumbHost()) + "/" + this.boardID + "/" + data.media.preview_orig),
theight: data.media.preview_h,
twidth: data.media.preview_w,
isSpoiler: data.media.spoiler === '1'
@@ -6346,6 +6378,7 @@ Post = (function() {
};
Post.prototype.addClone = function(context, contractThumb) {
+ Callbacks.Post.execute(this);
return new Post.Clone(this, context, contractThumb);
};
@@ -6828,7 +6861,7 @@ Redirect = (function() {
Redirect = {
archives: [
- { "uid": 3, "name": "4plebs", "domain": "archive.4plebs.org", "http": true, "https": true, "software": "foolfuuka", "boards": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ], "files": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ] },
+ { "uid": 3, "name": "4plebs", "domain": "archive.4plebs.org", "http": true, "https": true, "software": "foolfuuka", "boards": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ], "files": [ "adv", "f", "hr", "o", "pol", "s4s", "sp", "tg", "trv", "tv", "x" ], "reports": true },
{ "uid": 4, "name": "Nyafuu Archive", "domain": "archive.nyafuu.org", "http": false, "https": true, "software": "foolfuuka", "boards": [ "asp", "c", "e", "n", "news", "out", "p", "toy", "vp", "w", "wg", "wsr" ], "files": [ "asp", "c", "e", "n", "news", "out", "p", "toy", "vp", "w", "wg", "wsr" ] },
{ "uid": 8, "name": "Rebecca Black Tech", "domain": "archive.rebeccablacktech.com", "http": false, "https": true, "software": "fuuka", "boards": [ "cgl", "g", "mu" ], "files": [ "cgl", "g", "mu" ] },
{ "uid": 10, "name": "warosu", "domain": "warosu.org", "http": false, "https": true, "software": "fuuka", "boards": [ "3", "biz", "cgl", "ck", "diy", "fa", "g", "ic", "jp", "lit", "sci", "tg", "vr" ], "files": [ "3", "biz", "cgl", "ck", "diy", "fa", "g", "ic", "jp", "lit", "sci", "tg", "vr" ] },
@@ -6839,7 +6872,8 @@ Redirect = (function() {
{ "uid": 28, "name": "bstats", "domain": "archive.b-stats.org", "http": false, "https": true, "software": "foolfuuka", "boards": [ "f", "cm", "hm", "lgbt", "news", "qst", "trash", "y" ], "files": [] },
{ "uid": 29, "name": "Archived.Moe", "domain": "archived.moe", "http": true, "https": false, "software": "foolfuuka", "boards": [ "3", "a", "aco", "adv", "an", "asp", "b", "biz", "c", "cgl", "ck", "cm", "co", "d", "diy", "e", "f", "fa", "fit", "g", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "int", "jp", "k", "lgbt", "lit", "m", "mlp", "mu", "n", "news", "o", "out", "p", "po", "pol", "qa", "qst", "r", "r9k", "s", "s4s", "sci", "soc", "sp", "t", "tg", "toy", "trash", "trv", "tv", "u", "v", "vg", "vip", "vp", "vr", "w", "wg", "wsg", "wsr", "x", "y" ], "files": [ "gd", "po", "qst", "vip" ], "search": [ "aco", "adv", "an", "asp", "b", "c", "cgl", "ck", "cm", "con", "d", "diy", "e", "f", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "lgbt", "lit", "n", "news", "o", "out", "p", "po", "q", "qa", "qst", "r", "s", "soc", "trv", "u", "vip", "w", "wg", "wsg", "wsr", "x", "y" ] },
{ "uid": 30, "name": "TheBArchive.com", "domain": "thebarchive.com", "http": true, "https": false, "software": "foolfuuka", "boards": [ "b" ], "files": [ "b" ] },
- { "uid": 31, "name": "Archive Of Sins", "domain": "archiveofsins.com", "http": true, "https": false, "software": "foolfuuka", "boards": [ "h", "hc", "hm", "r", "s", "soc" ], "files": [ "h", "hc", "hm", "r", "s", "soc" ] }
+ { "uid": 31, "name": "Archive Of Sins", "domain": "archiveofsins.com", "http": true, "https": false, "software": "foolfuuka", "boards": [ "h", "hc", "hm", "r", "s", "soc" ], "files": [ "h", "hc", "hm", "r", "s", "soc" ] },
+ { "uid": 32, "name": "4tan", "domain": "boards.4tan.org", "http": false, "https": true, "software": "foolfuuka", "boards": [ "3", "a", "aco", "adv", "an", "asp", "b", "biz", "c", "cgl", "ck", "cm", "co", "d", "diy", "e", "f", "fa", "fit", "g", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "int", "jp", "k", "lgbt", "lit", "m", "mlp", "mu", "n", "news", "o", "out", "p", "po", "pol", "qa", "qst", "r", "r9k", "s", "s4s", "sci", "soc", "sp", "t", "tg", "toy", "trash", "trv", "tv", "u", "v", "vg", "vip", "vp", "vr", "w", "wg", "wsg", "wsr", "x", "y" ], "files": [], "search": [], "reports": true }
],
init: function() {
var now, ref;
@@ -7030,6 +7064,9 @@ Redirect = (function() {
file: function(archive, arg) {
var boardID, filename;
boardID = arg.boardID, filename = arg.filename;
+ if (boardID === 'f') {
+ filename = encodeURIComponent(Build.unescape(decodeURIComponent(filename)));
+ }
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + boardID + "/full_image/" + filename;
},
board: function(archive, arg) {
@@ -7058,6 +7095,19 @@ Redirect = (function() {
path = archive.software === 'foolfuuka' ? boardID + "/search/" + type + "/" + value + "/" : type === 'image' ? boardID + "/image/" + value : boardID + "/?task=search2&search_" + type + "=" + value;
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
},
+ report: function(boardID) {
+ var archive, boards, domain, https, j, len, name, ref, reports, software, urls;
+ urls = [];
+ ref = Conf['archives'];
+ for (j = 0, len = ref.length; j < len; j++) {
+ archive = ref[j];
+ software = archive.software, https = archive.https, reports = archive.reports, boards = archive.boards, name = archive.name, domain = archive.domain;
+ if (software === 'foolfuuka' && https && reports && boards instanceof Array && indexOf.call(boards, boardID) >= 0) {
+ urls.push([name, "https://" + domain + "/_/api/chan/offsite_report/"]);
+ }
+ }
+ return urls;
+ },
securityCheck: function(url) {
return /^https:\/\//.test(url) || location.protocol === 'http:' || Conf['Exempt Archives from Encryption'];
},
@@ -11020,7 +11070,7 @@ Settings = (function() {
}
},
upgrade: function(data, version) {
- var addCSS, addSauces, boardID, changes, compareString, j, k, key, len, len1, name, record, ref, ref1, ref2, ref3, ref4, ref5, ref6, rice, set, setD, type, uids, value;
+ var addCSS, addSauces, boardID, changes, compareString, j, k, key, known, len, len1, name, record, ref, ref1, ref2, ref3, ref4, ref5, ref6, rice, set, setD, type, uids, value;
changes = {};
set = function(key, value) {
return data[key] = changes[key] = value;
@@ -11207,6 +11257,14 @@ Settings = (function() {
if (compareString < '00001.00013.00001.00002') {
addSauces(['#//www.bing.com/images/search?q=imgurl:%IMG&view=detailv2&iss=sbi#enterInsights']);
}
+ if (compareString < '00001.00013.00005.00000') {
+ if (data['sauces'] != null) {
+ set('sauces', data['sauces'].replace(/^(#?\s*)http:\/\/regex\.info\/exif\.cgi/mg, '$1http://exif.regex.info/exif.cgi'));
+ if ((known = Config['sauces'].match(/# Known filename formats:(?:\n.+)*/))) {
+ set('sauces', data['sauces'] + '\n\n' + known[0]);
+ }
+ }
+ }
return changes;
},
loadSettings: function(data, cb) {
@@ -11267,7 +11325,7 @@ Settings = (function() {
sauce: function(section) {
var ta;
$.extend(section, {
- innerHTML: "Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
These parameters will be replaced by their corresponding values:%TURL: Thumbnail URL.%URL: Full image URL.%IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.%MD5: MD5 hash in base64.%sMD5: MD5 hash in base64 using - and _.%hMD5: MD5 hash in hexadecimal.%name: Original file name.%board: Current board.%%, %semi: Literal % and ;.
"
+ innerHTML: "Sauce is disabled.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
You can specify a regular expression the filename must match by appending ;regexp:[regular expression].
These parameters will be replaced by their corresponding values in the URL and displayed text:%TURL: Thumbnail URL.%URL: Full image URL.%IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.%MD5: MD5 hash in base64.%sMD5: MD5 hash in base64 using - and _.%hMD5: MD5 hash in hexadecimal.%name: Original file name.%board: Current board.%$0: The matched regular expression.%$1, %$2, and so on: Subexpressions within the matched regular expression.%%, %semi: Literal % and ;.
"
});
$('.warning', section).hidden = Conf['Sauce'];
ta = $('textarea', section);
@@ -13683,7 +13741,7 @@ Sauce = (function() {
Sauce = {
init: function() {
- var err, j, len, link, links, ref, ref1;
+ var j, len, link, linkData, links, ref, ref1;
if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Sauce'])) {
return;
}
@@ -13691,12 +13749,8 @@ Sauce = (function() {
ref1 = Conf['sauces'].split('\n');
for (j = 0, len = ref1.length; j < len; j++) {
link = ref1[j];
- try {
- if (link[0] !== '#') {
- links.push(link.trim());
- }
- } catch (_error) {
- err = _error;
+ if (link[0] !== '#' && (linkData = this.parseLink(link))) {
+ links.push(linkData);
}
}
if (!links.length) {
@@ -13712,13 +13766,13 @@ Sauce = (function() {
cb: this.node
});
},
- createSauceLink: function(link, post) {
- var a, ext, i, j, key, len, m, part, parts, ref, ref1, ref2, skip;
+ parseLink: function(link) {
+ var err, i, j, len, m, part, parts, ref, ref1, regexp;
if (!(link = link.trim())) {
return null;
}
parts = {};
- ref = link.split(/;(?=(?:text|boards|types|sandbox):?)/);
+ ref = link.split(/;(?=(?:text|boards|types|regexp|sandbox):?)/);
for (i = j = 0, len = ref.length; j < len; i = ++j) {
part = ref[i];
if (i === 0) {
@@ -13729,15 +13783,52 @@ Sauce = (function() {
}
}
parts['text'] || (parts['text'] = ((ref1 = parts['url'].match(/(\w+)\.\w+\//)) != null ? ref1[1] : void 0) || '?');
+ if ('regexp' in parts) {
+ try {
+ if ((regexp = parts['regexp'].match(/^\/(.*)\/(\w*)$/))) {
+ parts['regexp'] = RegExp(regexp[1], regexp[2]);
+ } else {
+ parts['regexp'] = RegExp(parts['regexp']);
+ }
+ } catch (_error) {
+ err = _error;
+ new Notice('warning', [$.tn("Invalid regexp for Sauce link:"), $.el('br'), $.tn(link), $.el('br'), $.tn(err.message)], 60);
+ return null;
+ }
+ }
+ return parts;
+ },
+ createSauceLink: function(link, post) {
+ var a, ext, j, key, len, matches, parts, ref, ref1, skip;
ext = post.file.url.match(/[^.]*$/)[0];
+ parts = {};
+ $.extend(parts, link);
+ if (!(!parts['boards'] || (ref = post.board.ID, indexOf.call(parts['boards'].split(','), ref) >= 0))) {
+ return null;
+ }
+ if (!(!parts['types'] || indexOf.call(parts['types'].split(','), ext) >= 0)) {
+ return null;
+ }
+ if (!(!parts['regexp'] || (matches = post.file.name.match(parts['regexp'])))) {
+ return null;
+ }
skip = false;
- for (key in parts) {
- parts[key] = parts[key].replace(/%(T?URL|IMG|[sh]?MD5|board|name|%|semi)/g, function(_, parameter) {
+ ref1 = ['url', 'text'];
+ for (j = 0, len = ref1.length; j < len; j++) {
+ key = ref1[j];
+ parts[key] = parts[key].replace(/%(T?URL|IMG|[sh]?MD5|board|name|%|semi|\$\d+)/g, function(_, parameter) {
var type;
- type = Sauce.formatters[parameter](post, ext);
- if (type == null) {
- skip = true;
- return '';
+ if (parameter[0] === '$') {
+ if (!matches) {
+ return parameter;
+ }
+ type = matches[parameter.slice(1)];
+ } else {
+ type = Sauce.formatters[parameter](post, ext);
+ if (type == null) {
+ skip = true;
+ return '';
+ }
}
if (key === 'url' && (parameter !== '%' && parameter !== 'semi')) {
if (/^javascript:/i.test(parts['url'])) {
@@ -13751,12 +13842,6 @@ Sauce = (function() {
if (skip) {
return null;
}
- if (!(!parts['boards'] || (ref2 = post.board.ID, indexOf.call(parts['boards'].split(','), ref2) >= 0))) {
- return null;
- }
- if (!(!parts['types'] || indexOf.call(parts['types'].split(','), ext) >= 0)) {
- return null;
- }
a = Sauce.link.cloneNode(false);
a.href = parts['url'];
a.textContent = parts['text'];
@@ -15266,26 +15351,22 @@ ReportLink = (function() {
}
a = $.el('a', {
className: 'report-link',
- href: 'javascript:;'
+ href: 'javascript:;',
+ textContent: 'Report'
});
$.on(a, 'click', ReportLink.report);
return Menu.menu.addEntry({
el: a,
order: 10,
open: function(post) {
- if (!(post.isDead || (post.thread.isDead && !post.thread.isArchived))) {
- a.textContent = 'Report';
- ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post;
- if ((Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) || d.cookie.indexOf('pass_enabled=1') >= 0) {
- ReportLink.url += '&altc=1';
- ReportLink.dims = 'width=350,height=275';
- } else {
- ReportLink.dims = 'width=400,height=550';
- }
+ ReportLink.url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post;
+ if ((Conf['Use Recaptcha v1 in Reports'] && Main.jsEnabled) || d.cookie.indexOf('pass_enabled=1') >= 0) {
+ ReportLink.url += '&altc=1';
+ ReportLink.dims = 'width=350,height=275';
} else {
- ReportLink.url = '';
+ ReportLink.dims = 'width=400,height=550';
}
- return !!ReportLink.url;
+ return true;
}
});
},
@@ -17355,18 +17436,18 @@ Report = (function() {
},
ready: function() {
$.addStyle(CSS.report);
- if (!Conf['Use Recaptcha v1 in Reports'] && !Conf['Force Noscript Captcha'] && Main.jsEnabled) {
- return new MutationObserver(function() {
- Report.fit('iframe[src^="https://www.google.com/recaptcha/api2/frame"]');
- return Report.fit('body');
- }).observe(d.body, {
- childList: true,
- attributes: true,
- subtree: true
- });
- } else {
- return Report.fit('body');
+ if (Conf['Archive Report']) {
+ Report.archive();
}
+ new MutationObserver(function() {
+ Report.fit('iframe[src^="https://www.google.com/recaptcha/api2/frame"]');
+ return Report.fit('body');
+ }).observe(d.body, {
+ childList: true,
+ attributes: true,
+ subtree: true
+ });
+ return Report.fit('body');
},
fit: function(selector) {
var dy, el;
@@ -17377,6 +17458,111 @@ Report = (function() {
if (dy > 0) {
return window.resizeBy(0, dy);
}
+ },
+ archive: function() {
+ var enabled, fieldset, form, match, message, reason, submit, types, urls;
+ if (!(urls = Redirect.report(g.BOARD.ID)).length) {
+ return;
+ }
+ form = $('form');
+ types = $.id('reportTypes');
+ message = $('h3');
+ fieldset = $.el('fieldset', {
+ id: 'archive-report',
+ hidden: true
+ }, {
+ innerHTML: ""
+ });
+ enabled = $('#archive-report-enabled', fieldset);
+ reason = $('#archive-report-reason', fieldset);
+ submit = $('#archive-report-submit', fieldset);
+ if (form && types) {
+ fieldset.hidden = !$('[value=illegal]', types).checked;
+ $.on(types, 'change', function(e) {
+ fieldset.hidden = e.target.value !== 'illegal';
+ return Report.fit('body');
+ });
+ $.after(types, fieldset);
+ Report.fit('body');
+ $.one(form, 'submit', function(e) {
+ if (!fieldset.hidden && enabled.checked) {
+ e.preventDefault();
+ return Report.archiveSubmit(urls, reason.value, (function(_this) {
+ return function(results) {
+ _this.action = '#archiveresults=' + encodeURIComponent(JSON.stringify(results));
+ return _this.submit();
+ };
+ })(this));
+ }
+ });
+ } else if (message) {
+ enabled.checked = false;
+ fieldset.hidden = false;
+ $.on(enabled, 'change', function() {
+ return submit.hidden = !this.checked;
+ });
+ $.after(message, fieldset);
+ $.on(submit, 'click', function() {
+ return Report.archiveSubmit(urls, reason.value, Report.archiveResults);
+ });
+ }
+ if ((match = location.hash.match(/^#archiveresults=(.*)$/))) {
+ try {
+ return Report.archiveResults(JSON.parse(decodeURIComponent(match[1])));
+ } catch (_error) {}
+ }
+ },
+ archiveSubmit: function(urls, reason, cb) {
+ var fn, form, i, len, name, ref, results, url;
+ form = $.formData({
+ board: g.BOARD.ID,
+ num: Report.postID,
+ reason: reason
+ });
+ results = [];
+ fn = function(name, url) {
+ return $.ajax(url, {
+ responseType: 'json',
+ onloadend: function() {
+ results.push([
+ name, this.response || {
+ error: ''
+ }
+ ]);
+ if (results.length === urls.length) {
+ return cb(results);
+ }
+ }
+ }, {
+ form: form
+ });
+ };
+ for (i = 0, len = urls.length; i < len; i++) {
+ ref = urls[i], name = ref[0], url = ref[1];
+ fn(name, url);
+ }
+ },
+ archiveResults: function(results) {
+ var fieldset, i, len, line, name, ref, response;
+ fieldset = $.id('archive-report');
+ for (i = 0, len = results.length; i < len; i++) {
+ ref = results[i], name = ref[0], response = ref[1];
+ line = $.el('h3', {
+ className: 'archive-report-response'
+ });
+ if ('success' in response) {
+ $.addClass(line, 'archive-report-success');
+ line.textContent = name + ": " + response.success;
+ } else {
+ $.addClass(line, 'archive-report-error');
+ line.textContent = name + ": " + (response.error || 'Error reporting post.');
+ }
+ if (fieldset) {
+ $.before(fieldset, line);
+ } else {
+ $.add(d.body, line);
+ }
+ }
}
};
@@ -18931,8 +19117,9 @@ ThreadWatcher = (function() {
ref = [thread.OP].concat(slice.call(thread.OP.clones));
for (i = 0, len = ref.length; i < len; i++) {
post = ref[i];
- toggler = $('.watch-thread-link', post.nodes.info);
- ThreadWatcher.setToggler(toggler, isWatched);
+ if ((toggler = $('.watch-thread-link', post.nodes.info))) {
+ ThreadWatcher.setToggler(toggler, isWatched);
+ }
}
}
if (thread.catalogView) {
diff --git a/builds/4chan-X.zip b/builds/4chan-X.zip
index b98aef657..89cb5cd6c 100644
Binary files a/builds/4chan-X.zip and b/builds/4chan-X.zip differ
diff --git a/builds/updates-beta.xml b/builds/updates-beta.xml
index d53bca898..41d20c3e1 100644
--- a/builds/updates-beta.xml
+++ b/builds/updates-beta.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/builds/updates.xml b/builds/updates.xml
index 33d3e8298..fae488bf8 100644
--- a/builds/updates.xml
+++ b/builds/updates.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/version.json b/version.json
index 79944aa23..cb495be44 100644
--- a/version.json
+++ b/version.json
@@ -1,4 +1,4 @@
{
- "version": "1.13.4.1",
- "date": "2017-01-07T12:12:37.975Z"
+ "version": "1.13.5.0",
+ "date": "2017-01-24T05:07:56.713Z"
}
\ No newline at end of file