diff --git a/CHANGELOG.md b/CHANGELOG.md
index ebb8d41a6..e91ae2b58 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,8 +2,63 @@ Sometimes the changelog has notes (not comprehensive) acknowledging people's wor
The links to individual versions below are to copies of the script with the update URL removed. If you want automatic updates, install the script from the links on the [main page](https://github.com/ccd0/4chan-x).
+### v1.10.11
+
+**v1.10.11.1** *(2015-04-24)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.1/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.1/builds/4chan-X-noupdate.crx "Chromium version")]
+- Merge v1.10.10.3: Fix original post form not showing when JS is disabled.
+
+**v1.10.11.0** *(2015-04-24)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.0/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.11.0/builds/4chan-X-noupdate.crx "Chromium version")]
+- Based on v1.10.10.2.
+- Fix whitespace being stripped from the comment before filtering. This makes it possible to filter whitespace spam.
+
+### v1.10.10
+
+**v1.10.10.3** *(2015-04-24)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.3/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.3/builds/4chan-X-noupdate.crx "Chromium version")]
+- Fix original post form not showing when JS is disabled.
+
+**v1.10.10.2** *(2015-04-21)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.2/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.2/builds/4chan-X-noupdate.crx "Chromium version")]
+- Add focus indication to verify button in captcha popup.
+
+**v1.10.10.1** *(2015-04-19)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.1/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.1/builds/4chan-X-noupdate.crx "Chromium version")]
+- Merge v1.10.9.5: (thebladeee) Archive list: Transferred /w/ and /wg/ back to Nyafuu.
+
+**v1.10.10.0** *(2015-04-18)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.0/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.10.0/builds/4chan-X-noupdate.crx "Chromium version")]
+- Based on v1.10.9.4.
+- Make images in image captcha selectable with arrow keys.
+- Add `Captcha Fixes` option (on by default) to control whether 4chan X runs inside the Javascript-based captcha.
+
+### v1.10.9
+
+**v1.10.9.5** *(2015-04-19)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.5/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.5/builds/4chan-X-noupdate.crx "Chromium version")]
+- (thebladeee) Archive list: Transferred /w/ and /wg/ back to Nyafuu.
+
+**v1.10.9.4** *(2015-04-17)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.4/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.4/builds/4chan-X-noupdate.crx "Chromium version")]
+- (Hasumi) Update archive.moe: Add /gif/.
+
+**v1.10.9.3** *(2015-04-17)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.3/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.3/builds/4chan-X-noupdate.crx "Chromium version")]
+- Resize report window as needed instead of opening it huge at beginning.
+
+**v1.10.9.2** *(2015-04-16)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.2/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.2/builds/4chan-X-noupdate.crx "Chromium version")]
+- (aCarbon) Normal report size box if pass is logged in.
+- Clean up leftover Recaptcha iframes to prevent memory leak.
+
+**v1.10.9.1** *(2015-04-15)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.1/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.1/builds/4chan-X-noupdate.crx "Chromium version")]
+- Merge v1.10.8.11: Increase report window size to accomodate increasingly common image captchas.
+- Fix report window closing before redirect.
+
+**v1.10.9.0** *(2015-04-15)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.0/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.9.0/builds/4chan-X-noupdate.crx "Chromium version")]
+- Based on v1.10.8.10.
+- Support reporting posts to fgts archive.
+- Add capcode to archive search menu.
+
### v1.10.8
+**v1.10.8.11** *(2015-04-15)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.8.11/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.8.11/builds/4chan-X-noupdate.crx "Chromium version")]
+- Increase report window size to accomodate increasingly common image captchas.
+
+**v1.10.8.10** *(2015-04-13)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.8.10/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.8.10/builds/4chan-X-noupdate.crx "Chromium version")]
+- Fix unwanted focusing on the submit button if you focus on the comment field too soon after entering the captcha.
+
**v1.10.8.9** *(2015-04-13)* - [[Firefox](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.8.9/builds/4chan-X-noupdate.user.js "Firefox version")] [[Chromium](https://raw.githubusercontent.com/ccd0/4chan-x/1.10.8.9/builds/4chan-X-noupdate.crx "Chromium version")]
- (fgts) Remove /fit/ from fgts archive.
diff --git a/Gruntfile.coffee b/Gruntfile.coffee
index 188cf337b..ba1d5a8c3 100755
--- a/Gruntfile.coffee
+++ b/Gruntfile.coffee
@@ -1,4 +1,6 @@
+path = require 'path'
crx = require 'crx'
+JSZip = require 'jszip'
module.exports = (grunt) ->
grunt.util.linefeed = '\n'
@@ -234,25 +236,6 @@ module.exports = (grunt) ->
]
tasks: 'build'
- crx:
- prod:
- src: 'testbuilds/crx<%= pkg.meta.suffix[pkg.channel] %>/'
- dest: 'testbuilds/<%= pkg.name %><%= pkg.meta.suffix[pkg.channel] %>.crx'
- privateKey: '../<%= pkg.name %>-keys/<%= pkg.name %>.pem'
-
- compress:
- crx:
- options:
- archive: 'testbuilds/<%= pkg.name %><%= pkg.meta.suffix[pkg.channel] %>.crx.zip'
- level: 9
- pretty: true
- expand: true
- flatten: true
- src: 'testbuilds/crx<%= pkg.meta.suffix[pkg.channel] %>/*'
- dest: '/'
- date: '<%= pkg.meta.date %>'
- mode: parseInt('644', 8)
-
clean:
builds: 'builds'
testbuilds: 'testbuilds'
@@ -335,7 +318,7 @@ module.exports = (grunt) ->
grunt.registerTask 'build-crx-channel', [
'concat:crx'
'copy:crx'
- 'compress:crx'
+ 'zip-crx'
]
grunt.registerTask 'build-crx', [
@@ -354,6 +337,17 @@ module.exports = (grunt) ->
'clean:tmpcrx'
]
+ grunt.registerTask 'zip-crx', 'Pack CRX contents in ZIP file', ->
+ pkg = grunt.config 'pkg'
+ zip = new JSZip()
+ for file in grunt.file.expand "testbuilds/crx#{pkg.meta.suffix[pkg.channel]}/*"
+ zip.file path.basename(file), grunt.file.read(file, {encoding: null}), {date: new Date(pkg.meta.date)}
+ output = zip.generate
+ type: 'nodebuffer'
+ compression: 'DEFLATE'
+ compressionOptions: {level: 9}
+ grunt.file.write "testbuilds/#{pkg.name}#{pkg.meta.suffix[pkg.channel]}.crx.zip", output
+
grunt.registerTask 'sign-channel', 'Sign CRX package', (channel) ->
done = @async()
pkg = grunt.config 'pkg'
diff --git a/builds/4chan-X-beta.crx b/builds/4chan-X-beta.crx
index 755e3ff6f..2b8231c04 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 d51d96bc2..436e671dc 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.10.8.9
+// @version 1.10.11.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@@ -11,6 +11,7 @@
// @match *://a.4cdn.org/*
// @match *://i.4cdn.org/*
// @match https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*
+// @match https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*
// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc
// @grant GM_getValue
// @grant GM_setValue
diff --git a/builds/4chan-X-beta.user.js b/builds/4chan-X-beta.user.js
index c78dc1b86..2044b92ee 100644
--- a/builds/4chan-X-beta.user.js
+++ b/builds/4chan-X-beta.user.js
@@ -1,7 +1,7 @@
// Generated by CoffeeScript
// ==UserScript==
// @name 4chan X beta
-// @version 1.10.8.9
+// @version 1.10.11.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@@ -12,6 +12,7 @@
// @match *://a.4cdn.org/*
// @match *://i.4cdn.org/*
// @match https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*
+// @match https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*
// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc
// @grant GM_getValue
// @grant GM_setValue
@@ -109,7 +110,7 @@
'use strict';
(function() {
- var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, Build, Callbacks, Captcha, CatalogLinks, CatalogThread, Clone, Conf, Config, Connection, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, E, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, ImageCommon, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReportLink, RevealSpoilers, Sauce, Settings, ShimSet, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume, c, d, doc, g,
+ var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, Build, Callbacks, Captcha, CatalogLinks, CatalogThread, Clone, Conf, Config, Connection, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, E, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, ImageCommon, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, ShimSet, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume, c, d, doc, g,
slice = [].slice,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
@@ -127,6 +128,7 @@
'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.'],
'Except Archives from Encryption': [false, '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.'],
@@ -235,7 +237,8 @@
'Force Noscript Captcha': [false, 'Use the non-Javascript fallback captcha in the QR even if Javascript is enabled.', 1],
'Auto-load captcha': [false, 'Automatically load the captcha in the QR even if your post is empty.', 1],
'Post on Captcha Completion': [false, 'Submit the post immediately when the captcha is completed.', 1],
- 'Bottom QR Link': [true, 'Places a link on the bottom of threads to open the QR.', 1]
+ 'Bottom QR Link': [true, 'Places a link on the bottom of threads to open the QR.', 1],
+ 'Captcha Fixes': [true, 'Make captcha more keyboard-navigable.']
},
'Quote Links': {
'Quote Backlinks': [true, 'Add quote backlinks.'],
@@ -396,7 +399,7 @@
doc = d.documentElement;
g = {
- VERSION: '1.10.8.9',
+ VERSION: '1.10.11.1',
NAMESPACE: '4chan X.',
boards: {}
};
@@ -627,9 +630,11 @@
$.addStyle = function(css, id, test) {
var style;
style = $.el('style', {
- id: id,
textContent: css
});
+ if (id != null) {
+ style.id = id;
+ }
$.asap((function() {
return d.head && ((test == null) || test());
}), function() {
@@ -1291,28 +1296,33 @@
}
Post.prototype.parseComment = function() {
- var bq, k, len1, node, ref, spoilers;
+ var abbr, bq, commentDisplay, k, len1, len2, node, q, ref, spoilers;
this.nodes.comment.normalize();
bq = this.nodes.comment.cloneNode(true);
- ref = $$('.abbr, .exif, b, marquee', bq);
+ ref = $$('.abbr + br, .exif, b, .fortune', bq);
for (k = 0, len1 = ref.length; k < len1; k++) {
node = ref[k];
$.rm(node);
}
+ if (abbr = $('.abbr', bq)) {
+ $.rm(abbr);
+ }
this.info.comment = this.nodesToText(bq);
- spoilers = $$('s', bq);
- return this.info.commentSpoilered = (function() {
- var len2, q;
+ if (abbr) {
+ this.info.comment = this.info.comment.replace(/\n\n$/, '');
+ }
+ commentDisplay = this.info.comment;
+ if (!(Conf['Remove Spoilers'] || Conf['Reveal Spoilers'])) {
+ spoilers = $$('s', bq);
if (spoilers.length) {
for (q = 0, len2 = spoilers.length; q < len2; q++) {
node = spoilers[q];
$.replace(node, $.tn('[spoiler]'));
}
- return this.nodesToText(bq);
- } else {
- return this.info.comment;
+ commentDisplay = this.nodesToText(bq);
}
- }).call(this);
+ }
+ return this.info.commentDisplay = commentDisplay.trim().replace(/\s+$/gm, '');
};
Post.prototype.nodesToText = function(bq) {
@@ -1323,7 +1333,7 @@
while (node = nodes.snapshotItem(i++)) {
text += node.data || '\n';
}
- return text.trim().replace(/\s+$/gm, '');
+ return text;
};
Post.prototype.parseQuotes = function() {
@@ -4298,7 +4308,7 @@
threadExcerpt: function(thread) {
var OP, excerpt, ref;
OP = thread.OP;
- excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.comment.replace(/\n+/g, ' // ') || OP.info.nameBlock);
+ excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.commentDisplay.replace(/\n+/g, ' // ') || OP.info.nameBlock);
if (excerpt.length > 73) {
return excerpt.slice(0, 70) + "...";
}
@@ -5070,18 +5080,18 @@
},
node: function() {
var filter, k, key, len1, ref, ref1, result, value;
- if (this.isClone || this.isFetchedQuote) {
+ if (this.isClone) {
return;
}
for (key in Filter.filters) {
- if ((value = Filter[key](this)) !== false) {
+ if ((value = Filter[key](this)) != null) {
ref = Filter.filters[key];
for (k = 0, len1 = ref.length; k < len1; k++) {
filter = ref[k];
if (!(result = filter(value, this.isReply))) {
continue;
}
- if (result.hide) {
+ if (result.hide && !this.isFetchedQuote) {
if (this.isReply) {
PostHiding.hide(this, result.stub);
} else if (g.VIEW === 'index') {
@@ -5103,73 +5113,41 @@
}
},
name: function(post) {
- if ('name' in post.info) {
- return post.info.name;
- }
- return false;
+ return post.info.name;
},
uniqueID: function(post) {
- if ('uniqueID' in post.info) {
- return post.info.uniqueID;
- }
- return false;
+ return post.info.uniqueID;
},
tripcode: function(post) {
- if ('tripcode' in post.info) {
- return post.info.tripcode;
- }
- return false;
+ return post.info.tripcode;
},
capcode: function(post) {
- if ('capcode' in post.info) {
- return post.info.capcode;
- }
- return false;
+ return post.info.capcode;
},
subject: function(post) {
- if ('subject' in post.info) {
- return post.info.subject || false;
- }
- return false;
+ return post.info.subject || void 0;
},
comment: function(post) {
- if ('comment' in post.info) {
- return post.info.comment;
- }
- return false;
+ return post.info.comment;
},
flag: function(post) {
- if ('flag' in post.info) {
- return post.info.flag;
- }
- return false;
+ return post.info.flag;
},
filename: function(post) {
- if (post.file) {
- return post.file.name;
- }
- return false;
+ var ref;
+ return (ref = post.file) != null ? ref.name : void 0;
},
dimensions: function(post) {
- var file;
- file = post.file;
- if (file != null ? file.dimensions : void 0) {
- return file.dimensions;
- }
- return false;
+ var ref;
+ return (ref = post.file) != null ? ref.dimensions : void 0;
},
filesize: function(post) {
- if (post.file) {
- return post.file.size;
- }
- return false;
+ var ref;
+ return (ref = post.file) != null ? ref.size : void 0;
},
MD5: function(post) {
var ref;
- if ((ref = post.file) != null ? ref.MD5 : void 0) {
- return post.file.MD5;
- }
- return false;
+ return (ref = post.file) != null ? ref.MD5 : void 0;
},
menu: {
init: function() {
@@ -5209,7 +5187,7 @@
open: function(post) {
var value;
value = Filter[type](post);
- return value !== false;
+ return value != null;
}
};
},
@@ -6696,8 +6674,8 @@
if (g.VIEW === 'archive') {
return;
}
- $.globalEval('document.documentElement.dataset.jsEnabled = true;');
- noscript = Conf['Force Noscript Captcha'] || !doc.dataset.jsEnabled;
+ $.globalEval('document.documentElement.classList.add("js-enabled");');
+ noscript = Conf['Force Noscript Captcha'] || !$.hasClass(doc, 'js-enabled');
this.captcha = Captcha[noscript ? 'noscript' : 'v2'];
$.on(d, '4chanXInitFinished', this.initReady);
Post.callbacks.push({
@@ -6726,7 +6704,7 @@
}
if (Conf['Hide Original Post Form']) {
$.addClass(doc, 'hide-original-post-form');
- if (!doc.dataset.jsEnabled) {
+ if (!$.hasClass(doc, 'js-enabled')) {
return $.onExists(doc, '#postForm noscript', true, $.rm);
}
}
@@ -7630,6 +7608,104 @@
Captcha = {};
+ Captcha.fixes = {
+ css: '.rc-imageselect-target > .rc-imageselect-tile > img:focus {\n outline: 2px solid #4a90e2;\n}\n.rc-button-default:focus {\n box-shadow: inset 0 0 0 2px #0063d6;\n}',
+ init: function() {
+ switch (location.pathname.split('/')[3]) {
+ case 'anchor':
+ return this.initMain();
+ case 'frame':
+ return this.initPopup();
+ }
+ },
+ initMain: function() {
+ return $.onExists(d.body, '#recaptcha-anchor', true, function(checkbox) {
+ var focus;
+ focus = function() {
+ if (d.hasFocus() && d.activeElement !== checkbox) {
+ return checkbox.focus();
+ }
+ };
+ focus();
+ return $.on(window, 'focus', function() {
+ return $.queueTask(focus);
+ });
+ });
+ },
+ initPopup: function() {
+ $.addStyle(this.css);
+ this.fixImages();
+ new MutationObserver((function(_this) {
+ return function() {
+ return _this.fixImages();
+ };
+ })(this)).observe(d.body, {
+ childList: true,
+ subtree: true
+ });
+ return $.on(d, 'keydown', this.keybinds.bind(this));
+ },
+ fixImages: function() {
+ var focus, img, k, len1, ref;
+ if (!(this.images = $$('.rc-imageselect-target > .rc-imageselect-tile > img')).length) {
+ return;
+ }
+ focus = this.images[0].tabIndex !== 0;
+ ref = this.images;
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ img = ref[k];
+ img.tabIndex = 0;
+ }
+ if (focus) {
+ return this.focusImage();
+ }
+ },
+ focusImage: function() {
+ var img;
+ img = this.images[0];
+ return $.asap(function() {
+ if (!doc.contains(img)) {
+ return true;
+ }
+ img.focus();
+ return d.activeElement === img;
+ }, function() {});
+ },
+ keybinds: function(e) {
+ var dx, reload, verify, x;
+ if (!(this.images && doc.contains(this.images[0]) && d.activeElement)) {
+ return;
+ }
+ reload = $.id('recaptcha-reload-button');
+ verify = $.id('recaptcha-verify-button');
+ x = this.images.indexOf(d.activeElement);
+ if (x < 0) {
+ if (!$('.rc-controls').contains(d.activeElement)) {
+ return;
+ }
+ x = d.activeElement === verify ? 11 : 9;
+ }
+ if (!(dx = {
+ 38: 9,
+ 40: 3,
+ 37: 11,
+ 39: 1
+ }[e.keyCode])) {
+ return;
+ }
+ x = (x + dx) % 12;
+ if (x === 10) {
+ x = dx === 11 ? 9 : 11;
+ }
+ (this.images[x] || {
+ 9: reload,
+ 11: verify
+ }[x]).focus();
+ e.preventDefault();
+ return e.stopPropagation();
+ }
+ };
+
Captcha.noscript = {
lifetime: 2 * $.MINUTE,
iframeURL: '//www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc',
@@ -7989,20 +8065,6 @@
};
})(this));
},
- initFrame: function() {
- $.globalEval('window.focus = function() {};');
- return $.on(window, 'focus', function() {
- return $.queueTask(function() {
- var checkbox;
- if (!(d.hasFocus() && (checkbox = $.id('recaptcha-anchor')))) {
- return;
- }
- if (d.activeElement !== checkbox) {
- return checkbox.focus();
- }
- });
- });
- },
shouldFocus: false,
timeouts: {},
postsCount: 0,
@@ -8102,6 +8164,7 @@
})(this));
},
destroy: function() {
+ var garbage, ins, k, len1, ref;
if (!this.isEnabled) {
return;
}
@@ -8110,7 +8173,15 @@
if (this.nodes.container) {
$.rm(this.nodes.container);
}
- return delete this.nodes.container;
+ delete this.nodes.container;
+ ref = $$('div > .gc-bubbleDefault');
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ garbage = ref[k];
+ if ((ins = garbage.parentNode.nextSibling) && ins.nodeName === 'INS') {
+ $.rm(ins);
+ }
+ $.rm(garbage.parentNode);
+ }
},
sync: function(captchas) {
if (captchas == null) {
@@ -8132,7 +8203,7 @@
}
},
save: function(pasted) {
- var base1;
+ var base1, focus, ref, ref1;
$.forceSync('captchas');
this.captchas.push({
response: $('textarea', this.nodes.container).value,
@@ -8148,6 +8219,7 @@
}
this.reload();
} else {
+ focus = ((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && ((ref1 = d.activeElement.src) != null ? ref1.slice(0, 38) : void 0) === 'https://www.google.com/recaptcha/api2/';
if (pasted) {
this.destroy();
} else {
@@ -8155,7 +8227,9 @@
base1.destroy = setTimeout(this.destroy.bind(this), 3 * $.SECOND);
}
}
- QR.nodes.status.focus();
+ if (focus) {
+ QR.nodes.status.focus();
+ }
}
if (Conf['Post on Captcha Completion'] && !QR.cooldown.auto) {
return QR.submit();
@@ -11441,7 +11515,7 @@
},
subEntries: []
};
- ref1 = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']];
+ ref1 = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['Capcode', 'capcode'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']];
for (k = 0, len1 = ref1.length; k < len1; k++) {
type = ref1[k];
entry.subEntries.push(this.createSubEntry(type[0], type[1]));
@@ -11708,17 +11782,19 @@
el: a,
order: 10,
open: function(post) {
- ReportLink.post = post;
- return !post.isDead;
+ ReportLink.url = !post.isDead ? "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post : Conf['Archive Report'] ? Redirect.to('report', {
+ boardID: post.board.ID,
+ postID: post.ID
+ }) : void 0;
+ return !!ReportLink.url;
}
});
},
report: function() {
- var id, post, set, url;
- post = ReportLink.post;
- url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post;
+ var id, set, url;
+ url = ReportLink.url;
id = Date.now();
- set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=285";
+ set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=685,height=200";
return window.open(url, id, set);
}
};
@@ -13271,7 +13347,7 @@
return;
}
notif = new Notification(post.info.nameBlock + " replied to you", {
- body: post.info[Conf['Remove Spoilers'] || Conf['Reveal Spoilers'] ? 'comment' : 'commentSpoilered'],
+ body: post.info.commentDisplay,
icon: Favicon.logo
});
notif.onclick = function() {
@@ -13411,7 +13487,8 @@
o = {
thread: {},
post: {},
- file: {}
+ file: {},
+ report: {}
};
archives = {};
ref = Redirect.archives;
@@ -13421,17 +13498,19 @@
archives[name] = data;
for (q = 0, len2 = boards.length; q < len2; q++) {
boardID = boards[q];
- if (!(!withCredentials)) {
- continue;
+ if (!withCredentials) {
+ if (!(boardID in o.thread)) {
+ o.thread[boardID] = data;
+ }
+ if (!(boardID in o.post || software !== 'foolfuuka')) {
+ o.post[boardID] = data;
+ }
+ if (!(boardID in o.file || indexOf.call(files, boardID) < 0)) {
+ o.file[boardID] = data;
+ }
}
- if (!(boardID in o.thread)) {
- o.thread[boardID] = data;
- }
- if (!(boardID in o.post || software !== 'foolfuuka')) {
- o.post[boardID] = data;
- }
- if (!(boardID in o.file || indexOf.call(files, boardID) < 0)) {
- o.file[boardID] = data;
+ if (name === 'fgts') {
+ o.report[boardID] = data;
}
}
}
@@ -13452,7 +13531,7 @@
}
return Redirect.data = o;
},
- archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","biz","c","co","diy","fit","gd","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u","w","wg"],"files":["c","d","e","i","lgbt","t","u","w","wg"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"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"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}],
+ archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","biz","c","co","diy","fit","gd","gif","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u"],"files":["c","d","e","i","lgbt","t","u"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"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"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}],
to: function(dest, data) {
var archive;
archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID];
@@ -13506,10 +13585,20 @@
var boardID, path, type, value;
boardID = arg.boardID, type = arg.type, value = arg.value;
type = type === 'name' ? 'username' : type === 'MD5' ? 'image' : type;
+ if (type === 'capcode') {
+ value = {
+ 'Developer': 'dev'
+ }[value] || value.toLowerCase();
+ }
value = encodeURIComponent(value);
path = archive.software === 'foolfuuka' ? boardID + "/search/" + type + "/" + value : boardID + "/?task=search2&search_" + (type === 'image' ? 'media_hash' : type) + "=" + value;
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
},
+ report: function(archive, arg) {
+ var boardID, postID;
+ boardID = arg.boardID, postID = arg.postID;
+ return "https://so.fgts.jp/report/?board=" + boardID + "&no=" + postID;
+ },
securityCheck: function(URL) {
return /^https:\/\//.test(URL) || location.protocol === 'http:' || Conf['Except Archives from Encryption'];
},
@@ -15229,6 +15318,63 @@
}
};
+ Report = {
+ init: function() {
+ var match;
+ if (!(/\bmode=report\b/.test(location.search) && (match = location.search.match(/\bno=(\d+)/)))) {
+ return;
+ }
+ this.postID = +match[1];
+ return $.ready(this.ready);
+ },
+ ready: function() {
+ new MutationObserver(Report.resize).observe(d.body, {
+ childList: true,
+ attributes: true,
+ subtree: true
+ });
+ if (Conf['Archive Report']) {
+ return Report.archive();
+ }
+ },
+ resize: function() {
+ var bubble, dy;
+ if (!(bubble = $('.gc-bubbleDefault'))) {
+ return;
+ }
+ dy = bubble.getBoundingClientRect().bottom - doc.clientHeight;
+ if (dy > 0) {
+ return window.resizeBy(0, dy);
+ }
+ },
+ archive: function() {
+ var link, message, url;
+ Redirect.init();
+ if (!(url = Redirect.to('report', {
+ boardID: g.BOARD.ID,
+ postID: Report.postID
+ }))) {
+ return;
+ }
+ if ((message = $('h3')) && /Report submitted!/.test(message.textContent)) {
+ $.globalEval('self.close = function(){};');
+ window.resizeTo(685, 320);
+ location.replace(url);
+ return;
+ }
+ link = $.el('a', {
+ href: url,
+ textContent: 'Report to fgts'
+ });
+ $.on(link, 'click', function(e) {
+ if (!(e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0)) {
+ return window.resizeTo(685, 320);
+ }
+ });
+ return $.add(d.body, [$.tn(' ['), link, $.tn(']')]);
+ }
+ };
+
Time = {
init: function() {
var ref;
@@ -16018,12 +16164,24 @@
Main = {
init: function() {
- var db, flatten, k, len1, pathname, ref, ref1, ref2, type;
+ var db, flatten, k, len1, pathname, ref, ref1, ref2;
if (location.hostname === 'www.google.com') {
- type = location.pathname === '/recaptcha/api/fallback' ? 'noscript' : 'v2';
- return $.ready(function() {
- return Captcha[type].initFrame();
- });
+ if (location.pathname === '/recaptcha/api/fallback') {
+ $.ready(function() {
+ return Captcha.noscript.initFrame();
+ });
+ } else {
+ $.get('Captcha Fixes', true, function(arg) {
+ var enabled;
+ enabled = arg['Captcha Fixes'];
+ if (enabled) {
+ return $.ready(function() {
+ return Captcha.fixes.init();
+ });
+ }
+ });
+ }
+ return;
}
g.threads = new SimpleDict();
g.posts = new SimpleDict();
@@ -16097,6 +16255,7 @@
case 'a.4cdn.org':
return;
case 'sys.4chan.org':
+ Report.init();
if (g.VIEW === 'post') {
PostSuccessful.init();
}
@@ -17096,10 +17255,8 @@
" /* party hats */\n" +
" pointer-events: none;\n" +
"}\n" +
-"marquee,\n" +
-".postMessage marquee + br,\n" +
-".postMessage marquee + br + br {\n" +
-" display: none;\n" +
+":root:not(.js-enabled) #postForm {\n" +
+" display: table;\n" +
"}\n" +
"/* Anti-autoplay */\n" +
"audio.controls-added {\n" +
diff --git a/builds/4chan-X-noupdate.crx b/builds/4chan-X-noupdate.crx
index 47840c735..e2a5bfd2a 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 d2066020b..831d8bd32 100644
--- a/builds/4chan-X-noupdate.user.js
+++ b/builds/4chan-X-noupdate.user.js
@@ -1,7 +1,7 @@
// Generated by CoffeeScript
// ==UserScript==
// @name 4chan X
-// @version 1.10.8.9
+// @version 1.10.11.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@@ -12,6 +12,7 @@
// @match *://a.4cdn.org/*
// @match *://i.4cdn.org/*
// @match https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*
+// @match https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*
// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc
// @grant GM_getValue
// @grant GM_setValue
@@ -108,7 +109,7 @@
'use strict';
(function() {
- var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, Build, Callbacks, Captcha, CatalogLinks, CatalogThread, Clone, Conf, Config, Connection, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, E, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, ImageCommon, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReportLink, RevealSpoilers, Sauce, Settings, ShimSet, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume, c, d, doc, g,
+ var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, Build, Callbacks, Captcha, CatalogLinks, CatalogThread, Clone, Conf, Config, Connection, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, E, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, ImageCommon, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, ShimSet, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume, c, d, doc, g,
slice = [].slice,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
@@ -126,6 +127,7 @@
'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.'],
'Except Archives from Encryption': [false, '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.'],
@@ -234,7 +236,8 @@
'Force Noscript Captcha': [false, 'Use the non-Javascript fallback captcha in the QR even if Javascript is enabled.', 1],
'Auto-load captcha': [false, 'Automatically load the captcha in the QR even if your post is empty.', 1],
'Post on Captcha Completion': [false, 'Submit the post immediately when the captcha is completed.', 1],
- 'Bottom QR Link': [true, 'Places a link on the bottom of threads to open the QR.', 1]
+ 'Bottom QR Link': [true, 'Places a link on the bottom of threads to open the QR.', 1],
+ 'Captcha Fixes': [true, 'Make captcha more keyboard-navigable.']
},
'Quote Links': {
'Quote Backlinks': [true, 'Add quote backlinks.'],
@@ -395,7 +398,7 @@
doc = d.documentElement;
g = {
- VERSION: '1.10.8.9',
+ VERSION: '1.10.11.1',
NAMESPACE: '4chan X.',
boards: {}
};
@@ -626,9 +629,11 @@
$.addStyle = function(css, id, test) {
var style;
style = $.el('style', {
- id: id,
textContent: css
});
+ if (id != null) {
+ style.id = id;
+ }
$.asap((function() {
return d.head && ((test == null) || test());
}), function() {
@@ -1290,28 +1295,33 @@
}
Post.prototype.parseComment = function() {
- var bq, k, len1, node, ref, spoilers;
+ var abbr, bq, commentDisplay, k, len1, len2, node, q, ref, spoilers;
this.nodes.comment.normalize();
bq = this.nodes.comment.cloneNode(true);
- ref = $$('.abbr, .exif, b, marquee', bq);
+ ref = $$('.abbr + br, .exif, b, .fortune', bq);
for (k = 0, len1 = ref.length; k < len1; k++) {
node = ref[k];
$.rm(node);
}
+ if (abbr = $('.abbr', bq)) {
+ $.rm(abbr);
+ }
this.info.comment = this.nodesToText(bq);
- spoilers = $$('s', bq);
- return this.info.commentSpoilered = (function() {
- var len2, q;
+ if (abbr) {
+ this.info.comment = this.info.comment.replace(/\n\n$/, '');
+ }
+ commentDisplay = this.info.comment;
+ if (!(Conf['Remove Spoilers'] || Conf['Reveal Spoilers'])) {
+ spoilers = $$('s', bq);
if (spoilers.length) {
for (q = 0, len2 = spoilers.length; q < len2; q++) {
node = spoilers[q];
$.replace(node, $.tn('[spoiler]'));
}
- return this.nodesToText(bq);
- } else {
- return this.info.comment;
+ commentDisplay = this.nodesToText(bq);
}
- }).call(this);
+ }
+ return this.info.commentDisplay = commentDisplay.trim().replace(/\s+$/gm, '');
};
Post.prototype.nodesToText = function(bq) {
@@ -1322,7 +1332,7 @@
while (node = nodes.snapshotItem(i++)) {
text += node.data || '\n';
}
- return text.trim().replace(/\s+$/gm, '');
+ return text;
};
Post.prototype.parseQuotes = function() {
@@ -4297,7 +4307,7 @@
threadExcerpt: function(thread) {
var OP, excerpt, ref;
OP = thread.OP;
- excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.comment.replace(/\n+/g, ' // ') || OP.info.nameBlock);
+ excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.commentDisplay.replace(/\n+/g, ' // ') || OP.info.nameBlock);
if (excerpt.length > 73) {
return excerpt.slice(0, 70) + "...";
}
@@ -5069,18 +5079,18 @@
},
node: function() {
var filter, k, key, len1, ref, ref1, result, value;
- if (this.isClone || this.isFetchedQuote) {
+ if (this.isClone) {
return;
}
for (key in Filter.filters) {
- if ((value = Filter[key](this)) !== false) {
+ if ((value = Filter[key](this)) != null) {
ref = Filter.filters[key];
for (k = 0, len1 = ref.length; k < len1; k++) {
filter = ref[k];
if (!(result = filter(value, this.isReply))) {
continue;
}
- if (result.hide) {
+ if (result.hide && !this.isFetchedQuote) {
if (this.isReply) {
PostHiding.hide(this, result.stub);
} else if (g.VIEW === 'index') {
@@ -5102,73 +5112,41 @@
}
},
name: function(post) {
- if ('name' in post.info) {
- return post.info.name;
- }
- return false;
+ return post.info.name;
},
uniqueID: function(post) {
- if ('uniqueID' in post.info) {
- return post.info.uniqueID;
- }
- return false;
+ return post.info.uniqueID;
},
tripcode: function(post) {
- if ('tripcode' in post.info) {
- return post.info.tripcode;
- }
- return false;
+ return post.info.tripcode;
},
capcode: function(post) {
- if ('capcode' in post.info) {
- return post.info.capcode;
- }
- return false;
+ return post.info.capcode;
},
subject: function(post) {
- if ('subject' in post.info) {
- return post.info.subject || false;
- }
- return false;
+ return post.info.subject || void 0;
},
comment: function(post) {
- if ('comment' in post.info) {
- return post.info.comment;
- }
- return false;
+ return post.info.comment;
},
flag: function(post) {
- if ('flag' in post.info) {
- return post.info.flag;
- }
- return false;
+ return post.info.flag;
},
filename: function(post) {
- if (post.file) {
- return post.file.name;
- }
- return false;
+ var ref;
+ return (ref = post.file) != null ? ref.name : void 0;
},
dimensions: function(post) {
- var file;
- file = post.file;
- if (file != null ? file.dimensions : void 0) {
- return file.dimensions;
- }
- return false;
+ var ref;
+ return (ref = post.file) != null ? ref.dimensions : void 0;
},
filesize: function(post) {
- if (post.file) {
- return post.file.size;
- }
- return false;
+ var ref;
+ return (ref = post.file) != null ? ref.size : void 0;
},
MD5: function(post) {
var ref;
- if ((ref = post.file) != null ? ref.MD5 : void 0) {
- return post.file.MD5;
- }
- return false;
+ return (ref = post.file) != null ? ref.MD5 : void 0;
},
menu: {
init: function() {
@@ -5208,7 +5186,7 @@
open: function(post) {
var value;
value = Filter[type](post);
- return value !== false;
+ return value != null;
}
};
},
@@ -6695,8 +6673,8 @@
if (g.VIEW === 'archive') {
return;
}
- $.globalEval('document.documentElement.dataset.jsEnabled = true;');
- noscript = Conf['Force Noscript Captcha'] || !doc.dataset.jsEnabled;
+ $.globalEval('document.documentElement.classList.add("js-enabled");');
+ noscript = Conf['Force Noscript Captcha'] || !$.hasClass(doc, 'js-enabled');
this.captcha = Captcha[noscript ? 'noscript' : 'v2'];
$.on(d, '4chanXInitFinished', this.initReady);
Post.callbacks.push({
@@ -6725,7 +6703,7 @@
}
if (Conf['Hide Original Post Form']) {
$.addClass(doc, 'hide-original-post-form');
- if (!doc.dataset.jsEnabled) {
+ if (!$.hasClass(doc, 'js-enabled')) {
return $.onExists(doc, '#postForm noscript', true, $.rm);
}
}
@@ -7629,6 +7607,104 @@
Captcha = {};
+ Captcha.fixes = {
+ css: '.rc-imageselect-target > .rc-imageselect-tile > img:focus {\n outline: 2px solid #4a90e2;\n}\n.rc-button-default:focus {\n box-shadow: inset 0 0 0 2px #0063d6;\n}',
+ init: function() {
+ switch (location.pathname.split('/')[3]) {
+ case 'anchor':
+ return this.initMain();
+ case 'frame':
+ return this.initPopup();
+ }
+ },
+ initMain: function() {
+ return $.onExists(d.body, '#recaptcha-anchor', true, function(checkbox) {
+ var focus;
+ focus = function() {
+ if (d.hasFocus() && d.activeElement !== checkbox) {
+ return checkbox.focus();
+ }
+ };
+ focus();
+ return $.on(window, 'focus', function() {
+ return $.queueTask(focus);
+ });
+ });
+ },
+ initPopup: function() {
+ $.addStyle(this.css);
+ this.fixImages();
+ new MutationObserver((function(_this) {
+ return function() {
+ return _this.fixImages();
+ };
+ })(this)).observe(d.body, {
+ childList: true,
+ subtree: true
+ });
+ return $.on(d, 'keydown', this.keybinds.bind(this));
+ },
+ fixImages: function() {
+ var focus, img, k, len1, ref;
+ if (!(this.images = $$('.rc-imageselect-target > .rc-imageselect-tile > img')).length) {
+ return;
+ }
+ focus = this.images[0].tabIndex !== 0;
+ ref = this.images;
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ img = ref[k];
+ img.tabIndex = 0;
+ }
+ if (focus) {
+ return this.focusImage();
+ }
+ },
+ focusImage: function() {
+ var img;
+ img = this.images[0];
+ return $.asap(function() {
+ if (!doc.contains(img)) {
+ return true;
+ }
+ img.focus();
+ return d.activeElement === img;
+ }, function() {});
+ },
+ keybinds: function(e) {
+ var dx, reload, verify, x;
+ if (!(this.images && doc.contains(this.images[0]) && d.activeElement)) {
+ return;
+ }
+ reload = $.id('recaptcha-reload-button');
+ verify = $.id('recaptcha-verify-button');
+ x = this.images.indexOf(d.activeElement);
+ if (x < 0) {
+ if (!$('.rc-controls').contains(d.activeElement)) {
+ return;
+ }
+ x = d.activeElement === verify ? 11 : 9;
+ }
+ if (!(dx = {
+ 38: 9,
+ 40: 3,
+ 37: 11,
+ 39: 1
+ }[e.keyCode])) {
+ return;
+ }
+ x = (x + dx) % 12;
+ if (x === 10) {
+ x = dx === 11 ? 9 : 11;
+ }
+ (this.images[x] || {
+ 9: reload,
+ 11: verify
+ }[x]).focus();
+ e.preventDefault();
+ return e.stopPropagation();
+ }
+ };
+
Captcha.noscript = {
lifetime: 2 * $.MINUTE,
iframeURL: '//www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc',
@@ -7988,20 +8064,6 @@
};
})(this));
},
- initFrame: function() {
- $.globalEval('window.focus = function() {};');
- return $.on(window, 'focus', function() {
- return $.queueTask(function() {
- var checkbox;
- if (!(d.hasFocus() && (checkbox = $.id('recaptcha-anchor')))) {
- return;
- }
- if (d.activeElement !== checkbox) {
- return checkbox.focus();
- }
- });
- });
- },
shouldFocus: false,
timeouts: {},
postsCount: 0,
@@ -8101,6 +8163,7 @@
})(this));
},
destroy: function() {
+ var garbage, ins, k, len1, ref;
if (!this.isEnabled) {
return;
}
@@ -8109,7 +8172,15 @@
if (this.nodes.container) {
$.rm(this.nodes.container);
}
- return delete this.nodes.container;
+ delete this.nodes.container;
+ ref = $$('div > .gc-bubbleDefault');
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ garbage = ref[k];
+ if ((ins = garbage.parentNode.nextSibling) && ins.nodeName === 'INS') {
+ $.rm(ins);
+ }
+ $.rm(garbage.parentNode);
+ }
},
sync: function(captchas) {
if (captchas == null) {
@@ -8131,7 +8202,7 @@
}
},
save: function(pasted) {
- var base1;
+ var base1, focus, ref, ref1;
$.forceSync('captchas');
this.captchas.push({
response: $('textarea', this.nodes.container).value,
@@ -8147,6 +8218,7 @@
}
this.reload();
} else {
+ focus = ((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && ((ref1 = d.activeElement.src) != null ? ref1.slice(0, 38) : void 0) === 'https://www.google.com/recaptcha/api2/';
if (pasted) {
this.destroy();
} else {
@@ -8154,7 +8226,9 @@
base1.destroy = setTimeout(this.destroy.bind(this), 3 * $.SECOND);
}
}
- QR.nodes.status.focus();
+ if (focus) {
+ QR.nodes.status.focus();
+ }
}
if (Conf['Post on Captcha Completion'] && !QR.cooldown.auto) {
return QR.submit();
@@ -11440,7 +11514,7 @@
},
subEntries: []
};
- ref1 = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']];
+ ref1 = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['Capcode', 'capcode'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']];
for (k = 0, len1 = ref1.length; k < len1; k++) {
type = ref1[k];
entry.subEntries.push(this.createSubEntry(type[0], type[1]));
@@ -11707,17 +11781,19 @@
el: a,
order: 10,
open: function(post) {
- ReportLink.post = post;
- return !post.isDead;
+ ReportLink.url = !post.isDead ? "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post : Conf['Archive Report'] ? Redirect.to('report', {
+ boardID: post.board.ID,
+ postID: post.ID
+ }) : void 0;
+ return !!ReportLink.url;
}
});
},
report: function() {
- var id, post, set, url;
- post = ReportLink.post;
- url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post;
+ var id, set, url;
+ url = ReportLink.url;
id = Date.now();
- set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=285";
+ set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=685,height=200";
return window.open(url, id, set);
}
};
@@ -13270,7 +13346,7 @@
return;
}
notif = new Notification(post.info.nameBlock + " replied to you", {
- body: post.info[Conf['Remove Spoilers'] || Conf['Reveal Spoilers'] ? 'comment' : 'commentSpoilered'],
+ body: post.info.commentDisplay,
icon: Favicon.logo
});
notif.onclick = function() {
@@ -13410,7 +13486,8 @@
o = {
thread: {},
post: {},
- file: {}
+ file: {},
+ report: {}
};
archives = {};
ref = Redirect.archives;
@@ -13420,17 +13497,19 @@
archives[name] = data;
for (q = 0, len2 = boards.length; q < len2; q++) {
boardID = boards[q];
- if (!(!withCredentials)) {
- continue;
+ if (!withCredentials) {
+ if (!(boardID in o.thread)) {
+ o.thread[boardID] = data;
+ }
+ if (!(boardID in o.post || software !== 'foolfuuka')) {
+ o.post[boardID] = data;
+ }
+ if (!(boardID in o.file || indexOf.call(files, boardID) < 0)) {
+ o.file[boardID] = data;
+ }
}
- if (!(boardID in o.thread)) {
- o.thread[boardID] = data;
- }
- if (!(boardID in o.post || software !== 'foolfuuka')) {
- o.post[boardID] = data;
- }
- if (!(boardID in o.file || indexOf.call(files, boardID) < 0)) {
- o.file[boardID] = data;
+ if (name === 'fgts') {
+ o.report[boardID] = data;
}
}
}
@@ -13451,7 +13530,7 @@
}
return Redirect.data = o;
},
- archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","biz","c","co","diy","fit","gd","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u","w","wg"],"files":["c","d","e","i","lgbt","t","u","w","wg"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"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"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}],
+ archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","biz","c","co","diy","fit","gd","gif","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u"],"files":["c","d","e","i","lgbt","t","u"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"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"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}],
to: function(dest, data) {
var archive;
archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID];
@@ -13505,10 +13584,20 @@
var boardID, path, type, value;
boardID = arg.boardID, type = arg.type, value = arg.value;
type = type === 'name' ? 'username' : type === 'MD5' ? 'image' : type;
+ if (type === 'capcode') {
+ value = {
+ 'Developer': 'dev'
+ }[value] || value.toLowerCase();
+ }
value = encodeURIComponent(value);
path = archive.software === 'foolfuuka' ? boardID + "/search/" + type + "/" + value : boardID + "/?task=search2&search_" + (type === 'image' ? 'media_hash' : type) + "=" + value;
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
},
+ report: function(archive, arg) {
+ var boardID, postID;
+ boardID = arg.boardID, postID = arg.postID;
+ return "https://so.fgts.jp/report/?board=" + boardID + "&no=" + postID;
+ },
securityCheck: function(URL) {
return /^https:\/\//.test(URL) || location.protocol === 'http:' || Conf['Except Archives from Encryption'];
},
@@ -15228,6 +15317,63 @@
}
};
+ Report = {
+ init: function() {
+ var match;
+ if (!(/\bmode=report\b/.test(location.search) && (match = location.search.match(/\bno=(\d+)/)))) {
+ return;
+ }
+ this.postID = +match[1];
+ return $.ready(this.ready);
+ },
+ ready: function() {
+ new MutationObserver(Report.resize).observe(d.body, {
+ childList: true,
+ attributes: true,
+ subtree: true
+ });
+ if (Conf['Archive Report']) {
+ return Report.archive();
+ }
+ },
+ resize: function() {
+ var bubble, dy;
+ if (!(bubble = $('.gc-bubbleDefault'))) {
+ return;
+ }
+ dy = bubble.getBoundingClientRect().bottom - doc.clientHeight;
+ if (dy > 0) {
+ return window.resizeBy(0, dy);
+ }
+ },
+ archive: function() {
+ var link, message, url;
+ Redirect.init();
+ if (!(url = Redirect.to('report', {
+ boardID: g.BOARD.ID,
+ postID: Report.postID
+ }))) {
+ return;
+ }
+ if ((message = $('h3')) && /Report submitted!/.test(message.textContent)) {
+ $.globalEval('self.close = function(){};');
+ window.resizeTo(685, 320);
+ location.replace(url);
+ return;
+ }
+ link = $.el('a', {
+ href: url,
+ textContent: 'Report to fgts'
+ });
+ $.on(link, 'click', function(e) {
+ if (!(e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0)) {
+ return window.resizeTo(685, 320);
+ }
+ });
+ return $.add(d.body, [$.tn(' ['), link, $.tn(']')]);
+ }
+ };
+
Time = {
init: function() {
var ref;
@@ -16017,12 +16163,24 @@
Main = {
init: function() {
- var db, flatten, k, len1, pathname, ref, ref1, ref2, type;
+ var db, flatten, k, len1, pathname, ref, ref1, ref2;
if (location.hostname === 'www.google.com') {
- type = location.pathname === '/recaptcha/api/fallback' ? 'noscript' : 'v2';
- return $.ready(function() {
- return Captcha[type].initFrame();
- });
+ if (location.pathname === '/recaptcha/api/fallback') {
+ $.ready(function() {
+ return Captcha.noscript.initFrame();
+ });
+ } else {
+ $.get('Captcha Fixes', true, function(arg) {
+ var enabled;
+ enabled = arg['Captcha Fixes'];
+ if (enabled) {
+ return $.ready(function() {
+ return Captcha.fixes.init();
+ });
+ }
+ });
+ }
+ return;
}
g.threads = new SimpleDict();
g.posts = new SimpleDict();
@@ -16096,6 +16254,7 @@
case 'a.4cdn.org':
return;
case 'sys.4chan.org':
+ Report.init();
if (g.VIEW === 'post') {
PostSuccessful.init();
}
@@ -17095,10 +17254,8 @@
" /* party hats */\n" +
" pointer-events: none;\n" +
"}\n" +
-"marquee,\n" +
-".postMessage marquee + br,\n" +
-".postMessage marquee + br + br {\n" +
-" display: none;\n" +
+":root:not(.js-enabled) #postForm {\n" +
+" display: table;\n" +
"}\n" +
"/* Anti-autoplay */\n" +
"audio.controls-added {\n" +
diff --git a/builds/4chan-X.crx b/builds/4chan-X.crx
index 2bd888c58..412704a12 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 6f9db5ad5..384f8bbc0 100644
--- a/builds/4chan-X.meta.js
+++ b/builds/4chan-X.meta.js
@@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X
-// @version 1.10.8.9
+// @version 1.10.11.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@@ -11,6 +11,7 @@
// @match *://a.4cdn.org/*
// @match *://i.4cdn.org/*
// @match https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*
+// @match https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*
// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc
// @grant GM_getValue
// @grant GM_setValue
diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js
index ac91991ec..b0fe5afb7 100644
--- a/builds/4chan-X.user.js
+++ b/builds/4chan-X.user.js
@@ -1,7 +1,7 @@
// Generated by CoffeeScript
// ==UserScript==
// @name 4chan X
-// @version 1.10.8.9
+// @version 1.10.11.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@@ -12,6 +12,7 @@
// @match *://a.4cdn.org/*
// @match *://i.4cdn.org/*
// @match https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*
+// @match https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*
// @match *://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc
// @grant GM_getValue
// @grant GM_setValue
@@ -109,7 +110,7 @@
'use strict';
(function() {
- var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, Build, Callbacks, Captcha, CatalogLinks, CatalogThread, Clone, Conf, Config, Connection, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, E, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, ImageCommon, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReportLink, RevealSpoilers, Sauce, Settings, ShimSet, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume, c, d, doc, g,
+ var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, Build, Callbacks, Captcha, CatalogLinks, CatalogThread, Clone, Conf, Config, Connection, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, E, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, ImageCommon, ImageExpand, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, ShimSet, SimpleDict, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, Volume, c, d, doc, g,
slice = [].slice,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
@@ -127,6 +128,7 @@
'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.'],
'Except Archives from Encryption': [false, '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.'],
@@ -235,7 +237,8 @@
'Force Noscript Captcha': [false, 'Use the non-Javascript fallback captcha in the QR even if Javascript is enabled.', 1],
'Auto-load captcha': [false, 'Automatically load the captcha in the QR even if your post is empty.', 1],
'Post on Captcha Completion': [false, 'Submit the post immediately when the captcha is completed.', 1],
- 'Bottom QR Link': [true, 'Places a link on the bottom of threads to open the QR.', 1]
+ 'Bottom QR Link': [true, 'Places a link on the bottom of threads to open the QR.', 1],
+ 'Captcha Fixes': [true, 'Make captcha more keyboard-navigable.']
},
'Quote Links': {
'Quote Backlinks': [true, 'Add quote backlinks.'],
@@ -396,7 +399,7 @@
doc = d.documentElement;
g = {
- VERSION: '1.10.8.9',
+ VERSION: '1.10.11.1',
NAMESPACE: '4chan X.',
boards: {}
};
@@ -627,9 +630,11 @@
$.addStyle = function(css, id, test) {
var style;
style = $.el('style', {
- id: id,
textContent: css
});
+ if (id != null) {
+ style.id = id;
+ }
$.asap((function() {
return d.head && ((test == null) || test());
}), function() {
@@ -1291,28 +1296,33 @@
}
Post.prototype.parseComment = function() {
- var bq, k, len1, node, ref, spoilers;
+ var abbr, bq, commentDisplay, k, len1, len2, node, q, ref, spoilers;
this.nodes.comment.normalize();
bq = this.nodes.comment.cloneNode(true);
- ref = $$('.abbr, .exif, b, marquee', bq);
+ ref = $$('.abbr + br, .exif, b, .fortune', bq);
for (k = 0, len1 = ref.length; k < len1; k++) {
node = ref[k];
$.rm(node);
}
+ if (abbr = $('.abbr', bq)) {
+ $.rm(abbr);
+ }
this.info.comment = this.nodesToText(bq);
- spoilers = $$('s', bq);
- return this.info.commentSpoilered = (function() {
- var len2, q;
+ if (abbr) {
+ this.info.comment = this.info.comment.replace(/\n\n$/, '');
+ }
+ commentDisplay = this.info.comment;
+ if (!(Conf['Remove Spoilers'] || Conf['Reveal Spoilers'])) {
+ spoilers = $$('s', bq);
if (spoilers.length) {
for (q = 0, len2 = spoilers.length; q < len2; q++) {
node = spoilers[q];
$.replace(node, $.tn('[spoiler]'));
}
- return this.nodesToText(bq);
- } else {
- return this.info.comment;
+ commentDisplay = this.nodesToText(bq);
}
- }).call(this);
+ }
+ return this.info.commentDisplay = commentDisplay.trim().replace(/\s+$/gm, '');
};
Post.prototype.nodesToText = function(bq) {
@@ -1323,7 +1333,7 @@
while (node = nodes.snapshotItem(i++)) {
text += node.data || '\n';
}
- return text.trim().replace(/\s+$/gm, '');
+ return text;
};
Post.prototype.parseQuotes = function() {
@@ -4298,7 +4308,7 @@
threadExcerpt: function(thread) {
var OP, excerpt, ref;
OP = thread.OP;
- excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.comment.replace(/\n+/g, ' // ') || OP.info.nameBlock);
+ excerpt = ("/" + thread.board + "/ - ") + (((ref = OP.info.subject) != null ? ref.trim() : void 0) || OP.info.commentDisplay.replace(/\n+/g, ' // ') || OP.info.nameBlock);
if (excerpt.length > 73) {
return excerpt.slice(0, 70) + "...";
}
@@ -5070,18 +5080,18 @@
},
node: function() {
var filter, k, key, len1, ref, ref1, result, value;
- if (this.isClone || this.isFetchedQuote) {
+ if (this.isClone) {
return;
}
for (key in Filter.filters) {
- if ((value = Filter[key](this)) !== false) {
+ if ((value = Filter[key](this)) != null) {
ref = Filter.filters[key];
for (k = 0, len1 = ref.length; k < len1; k++) {
filter = ref[k];
if (!(result = filter(value, this.isReply))) {
continue;
}
- if (result.hide) {
+ if (result.hide && !this.isFetchedQuote) {
if (this.isReply) {
PostHiding.hide(this, result.stub);
} else if (g.VIEW === 'index') {
@@ -5103,73 +5113,41 @@
}
},
name: function(post) {
- if ('name' in post.info) {
- return post.info.name;
- }
- return false;
+ return post.info.name;
},
uniqueID: function(post) {
- if ('uniqueID' in post.info) {
- return post.info.uniqueID;
- }
- return false;
+ return post.info.uniqueID;
},
tripcode: function(post) {
- if ('tripcode' in post.info) {
- return post.info.tripcode;
- }
- return false;
+ return post.info.tripcode;
},
capcode: function(post) {
- if ('capcode' in post.info) {
- return post.info.capcode;
- }
- return false;
+ return post.info.capcode;
},
subject: function(post) {
- if ('subject' in post.info) {
- return post.info.subject || false;
- }
- return false;
+ return post.info.subject || void 0;
},
comment: function(post) {
- if ('comment' in post.info) {
- return post.info.comment;
- }
- return false;
+ return post.info.comment;
},
flag: function(post) {
- if ('flag' in post.info) {
- return post.info.flag;
- }
- return false;
+ return post.info.flag;
},
filename: function(post) {
- if (post.file) {
- return post.file.name;
- }
- return false;
+ var ref;
+ return (ref = post.file) != null ? ref.name : void 0;
},
dimensions: function(post) {
- var file;
- file = post.file;
- if (file != null ? file.dimensions : void 0) {
- return file.dimensions;
- }
- return false;
+ var ref;
+ return (ref = post.file) != null ? ref.dimensions : void 0;
},
filesize: function(post) {
- if (post.file) {
- return post.file.size;
- }
- return false;
+ var ref;
+ return (ref = post.file) != null ? ref.size : void 0;
},
MD5: function(post) {
var ref;
- if ((ref = post.file) != null ? ref.MD5 : void 0) {
- return post.file.MD5;
- }
- return false;
+ return (ref = post.file) != null ? ref.MD5 : void 0;
},
menu: {
init: function() {
@@ -5209,7 +5187,7 @@
open: function(post) {
var value;
value = Filter[type](post);
- return value !== false;
+ return value != null;
}
};
},
@@ -6696,8 +6674,8 @@
if (g.VIEW === 'archive') {
return;
}
- $.globalEval('document.documentElement.dataset.jsEnabled = true;');
- noscript = Conf['Force Noscript Captcha'] || !doc.dataset.jsEnabled;
+ $.globalEval('document.documentElement.classList.add("js-enabled");');
+ noscript = Conf['Force Noscript Captcha'] || !$.hasClass(doc, 'js-enabled');
this.captcha = Captcha[noscript ? 'noscript' : 'v2'];
$.on(d, '4chanXInitFinished', this.initReady);
Post.callbacks.push({
@@ -6726,7 +6704,7 @@
}
if (Conf['Hide Original Post Form']) {
$.addClass(doc, 'hide-original-post-form');
- if (!doc.dataset.jsEnabled) {
+ if (!$.hasClass(doc, 'js-enabled')) {
return $.onExists(doc, '#postForm noscript', true, $.rm);
}
}
@@ -7630,6 +7608,104 @@
Captcha = {};
+ Captcha.fixes = {
+ css: '.rc-imageselect-target > .rc-imageselect-tile > img:focus {\n outline: 2px solid #4a90e2;\n}\n.rc-button-default:focus {\n box-shadow: inset 0 0 0 2px #0063d6;\n}',
+ init: function() {
+ switch (location.pathname.split('/')[3]) {
+ case 'anchor':
+ return this.initMain();
+ case 'frame':
+ return this.initPopup();
+ }
+ },
+ initMain: function() {
+ return $.onExists(d.body, '#recaptcha-anchor', true, function(checkbox) {
+ var focus;
+ focus = function() {
+ if (d.hasFocus() && d.activeElement !== checkbox) {
+ return checkbox.focus();
+ }
+ };
+ focus();
+ return $.on(window, 'focus', function() {
+ return $.queueTask(focus);
+ });
+ });
+ },
+ initPopup: function() {
+ $.addStyle(this.css);
+ this.fixImages();
+ new MutationObserver((function(_this) {
+ return function() {
+ return _this.fixImages();
+ };
+ })(this)).observe(d.body, {
+ childList: true,
+ subtree: true
+ });
+ return $.on(d, 'keydown', this.keybinds.bind(this));
+ },
+ fixImages: function() {
+ var focus, img, k, len1, ref;
+ if (!(this.images = $$('.rc-imageselect-target > .rc-imageselect-tile > img')).length) {
+ return;
+ }
+ focus = this.images[0].tabIndex !== 0;
+ ref = this.images;
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ img = ref[k];
+ img.tabIndex = 0;
+ }
+ if (focus) {
+ return this.focusImage();
+ }
+ },
+ focusImage: function() {
+ var img;
+ img = this.images[0];
+ return $.asap(function() {
+ if (!doc.contains(img)) {
+ return true;
+ }
+ img.focus();
+ return d.activeElement === img;
+ }, function() {});
+ },
+ keybinds: function(e) {
+ var dx, reload, verify, x;
+ if (!(this.images && doc.contains(this.images[0]) && d.activeElement)) {
+ return;
+ }
+ reload = $.id('recaptcha-reload-button');
+ verify = $.id('recaptcha-verify-button');
+ x = this.images.indexOf(d.activeElement);
+ if (x < 0) {
+ if (!$('.rc-controls').contains(d.activeElement)) {
+ return;
+ }
+ x = d.activeElement === verify ? 11 : 9;
+ }
+ if (!(dx = {
+ 38: 9,
+ 40: 3,
+ 37: 11,
+ 39: 1
+ }[e.keyCode])) {
+ return;
+ }
+ x = (x + dx) % 12;
+ if (x === 10) {
+ x = dx === 11 ? 9 : 11;
+ }
+ (this.images[x] || {
+ 9: reload,
+ 11: verify
+ }[x]).focus();
+ e.preventDefault();
+ return e.stopPropagation();
+ }
+ };
+
Captcha.noscript = {
lifetime: 2 * $.MINUTE,
iframeURL: '//www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc',
@@ -7989,20 +8065,6 @@
};
})(this));
},
- initFrame: function() {
- $.globalEval('window.focus = function() {};');
- return $.on(window, 'focus', function() {
- return $.queueTask(function() {
- var checkbox;
- if (!(d.hasFocus() && (checkbox = $.id('recaptcha-anchor')))) {
- return;
- }
- if (d.activeElement !== checkbox) {
- return checkbox.focus();
- }
- });
- });
- },
shouldFocus: false,
timeouts: {},
postsCount: 0,
@@ -8102,6 +8164,7 @@
})(this));
},
destroy: function() {
+ var garbage, ins, k, len1, ref;
if (!this.isEnabled) {
return;
}
@@ -8110,7 +8173,15 @@
if (this.nodes.container) {
$.rm(this.nodes.container);
}
- return delete this.nodes.container;
+ delete this.nodes.container;
+ ref = $$('div > .gc-bubbleDefault');
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ garbage = ref[k];
+ if ((ins = garbage.parentNode.nextSibling) && ins.nodeName === 'INS') {
+ $.rm(ins);
+ }
+ $.rm(garbage.parentNode);
+ }
},
sync: function(captchas) {
if (captchas == null) {
@@ -8132,7 +8203,7 @@
}
},
save: function(pasted) {
- var base1;
+ var base1, focus, ref, ref1;
$.forceSync('captchas');
this.captchas.push({
response: $('textarea', this.nodes.container).value,
@@ -8148,6 +8219,7 @@
}
this.reload();
} else {
+ focus = ((ref = d.activeElement) != null ? ref.nodeName : void 0) === 'IFRAME' && ((ref1 = d.activeElement.src) != null ? ref1.slice(0, 38) : void 0) === 'https://www.google.com/recaptcha/api2/';
if (pasted) {
this.destroy();
} else {
@@ -8155,7 +8227,9 @@
base1.destroy = setTimeout(this.destroy.bind(this), 3 * $.SECOND);
}
}
- QR.nodes.status.focus();
+ if (focus) {
+ QR.nodes.status.focus();
+ }
}
if (Conf['Post on Captcha Completion'] && !QR.cooldown.auto) {
return QR.submit();
@@ -11441,7 +11515,7 @@
},
subEntries: []
};
- ref1 = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']];
+ ref1 = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['Capcode', 'capcode'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']];
for (k = 0, len1 = ref1.length; k < len1; k++) {
type = ref1[k];
entry.subEntries.push(this.createSubEntry(type[0], type[1]));
@@ -11708,17 +11782,19 @@
el: a,
order: 10,
open: function(post) {
- ReportLink.post = post;
- return !post.isDead;
+ ReportLink.url = !post.isDead ? "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post : Conf['Archive Report'] ? Redirect.to('report', {
+ boardID: post.board.ID,
+ postID: post.ID
+ }) : void 0;
+ return !!ReportLink.url;
}
});
},
report: function() {
- var id, post, set, url;
- post = ReportLink.post;
- url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post;
+ var id, set, url;
+ url = ReportLink.url;
id = Date.now();
- set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=285";
+ set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=685,height=200";
return window.open(url, id, set);
}
};
@@ -13271,7 +13347,7 @@
return;
}
notif = new Notification(post.info.nameBlock + " replied to you", {
- body: post.info[Conf['Remove Spoilers'] || Conf['Reveal Spoilers'] ? 'comment' : 'commentSpoilered'],
+ body: post.info.commentDisplay,
icon: Favicon.logo
});
notif.onclick = function() {
@@ -13411,7 +13487,8 @@
o = {
thread: {},
post: {},
- file: {}
+ file: {},
+ report: {}
};
archives = {};
ref = Redirect.archives;
@@ -13421,17 +13498,19 @@
archives[name] = data;
for (q = 0, len2 = boards.length; q < len2; q++) {
boardID = boards[q];
- if (!(!withCredentials)) {
- continue;
+ if (!withCredentials) {
+ if (!(boardID in o.thread)) {
+ o.thread[boardID] = data;
+ }
+ if (!(boardID in o.post || software !== 'foolfuuka')) {
+ o.post[boardID] = data;
+ }
+ if (!(boardID in o.file || indexOf.call(files, boardID) < 0)) {
+ o.file[boardID] = data;
+ }
}
- if (!(boardID in o.thread)) {
- o.thread[boardID] = data;
- }
- if (!(boardID in o.post || software !== 'foolfuuka')) {
- o.post[boardID] = data;
- }
- if (!(boardID in o.file || indexOf.call(files, boardID) < 0)) {
- o.file[boardID] = data;
+ if (name === 'fgts') {
+ o.report[boardID] = data;
}
}
}
@@ -13452,7 +13531,7 @@
}
return Redirect.data = o;
},
- archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","biz","c","co","diy","fit","gd","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u","w","wg"],"files":["c","d","e","i","lgbt","t","u","w","wg"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"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"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}],
+ archives: [{"uid":0,"name":"Moe","domain":"archive.moe","http":false,"https":true,"software":"foolfuuka","boards":["a","biz","c","co","diy","fit","gd","gif","h","i","int","jp","k","m","mlp","out","po","qa","r9k","s4s","sci","tg","tv","u","v","vg","vp","vr","wsg"],"files":["a","biz","c","co","diy","fit","gd","h","i","jp","k","m","mlp","po","qa","r9k","s4s","sci","tg","u","v","vg","vp","vr","wsg"]},{"uid":3,"name":"4plebs Archive","domain":"archive.4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"],"files":["adv","f","hr","o","pol","s4s","tg","trv","tv","x"]},{"uid":4,"name":"Nyafuu Archive","domain":"archive.nyafuu.org","http":true,"https":true,"software":"foolfuuka","boards":["c","e","w","wg"],"files":["c","e","w","wg"]},{"uid":5,"name":"Love is Over","domain":"archive.loveisover.me","http":true,"https":true,"software":"foolfuuka","boards":["c","d","e","i","lgbt","t","u"],"files":["c","d","e","i","lgbt","t","u"]},{"uid":8,"name":"Rebecca Black Tech","domain":"rbt.asia","http":false,"https":true,"software":"fuuka","boards":["cgl","g","mu","qa","w"],"files":["cgl","g","mu","qa","w"]},{"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"]},{"uid":15,"name":"fgts","domain":"fgts.jp","http":true,"https":true,"software":"foolfuuka","boards":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"],"files":["asp","b","cm","h","hc","hm","n","p","qa","r","s","soc","toy","y"]},{"uid":22,"name":"not4plebs","domain":"totally.not4plebs.org","http":true,"https":true,"software":"foolfuuka","boards":["sp"],"files":["sp"]},{"uid":23,"name":"DesuStorage","domain":"archive.desustorage.org","http":false,"https":true,"software":"foolfuuka","boards":["mlp","qa"],"files":["mlp","qa"]}],
to: function(dest, data) {
var archive;
archive = (dest === 'search' || dest === 'board' ? Redirect.data.thread : Redirect.data[dest])[data.boardID];
@@ -13506,10 +13585,20 @@
var boardID, path, type, value;
boardID = arg.boardID, type = arg.type, value = arg.value;
type = type === 'name' ? 'username' : type === 'MD5' ? 'image' : type;
+ if (type === 'capcode') {
+ value = {
+ 'Developer': 'dev'
+ }[value] || value.toLowerCase();
+ }
value = encodeURIComponent(value);
path = archive.software === 'foolfuuka' ? boardID + "/search/" + type + "/" + value : boardID + "/?task=search2&search_" + (type === 'image' ? 'media_hash' : type) + "=" + value;
return "" + (Redirect.protocol(archive)) + archive.domain + "/" + path;
},
+ report: function(archive, arg) {
+ var boardID, postID;
+ boardID = arg.boardID, postID = arg.postID;
+ return "https://so.fgts.jp/report/?board=" + boardID + "&no=" + postID;
+ },
securityCheck: function(URL) {
return /^https:\/\//.test(URL) || location.protocol === 'http:' || Conf['Except Archives from Encryption'];
},
@@ -15229,6 +15318,63 @@
}
};
+ Report = {
+ init: function() {
+ var match;
+ if (!(/\bmode=report\b/.test(location.search) && (match = location.search.match(/\bno=(\d+)/)))) {
+ return;
+ }
+ this.postID = +match[1];
+ return $.ready(this.ready);
+ },
+ ready: function() {
+ new MutationObserver(Report.resize).observe(d.body, {
+ childList: true,
+ attributes: true,
+ subtree: true
+ });
+ if (Conf['Archive Report']) {
+ return Report.archive();
+ }
+ },
+ resize: function() {
+ var bubble, dy;
+ if (!(bubble = $('.gc-bubbleDefault'))) {
+ return;
+ }
+ dy = bubble.getBoundingClientRect().bottom - doc.clientHeight;
+ if (dy > 0) {
+ return window.resizeBy(0, dy);
+ }
+ },
+ archive: function() {
+ var link, message, url;
+ Redirect.init();
+ if (!(url = Redirect.to('report', {
+ boardID: g.BOARD.ID,
+ postID: Report.postID
+ }))) {
+ return;
+ }
+ if ((message = $('h3')) && /Report submitted!/.test(message.textContent)) {
+ $.globalEval('self.close = function(){};');
+ window.resizeTo(685, 320);
+ location.replace(url);
+ return;
+ }
+ link = $.el('a', {
+ href: url,
+ textContent: 'Report to fgts'
+ });
+ $.on(link, 'click', function(e) {
+ if (!(e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0)) {
+ return window.resizeTo(685, 320);
+ }
+ });
+ return $.add(d.body, [$.tn(' ['), link, $.tn(']')]);
+ }
+ };
+
Time = {
init: function() {
var ref;
@@ -16018,12 +16164,24 @@
Main = {
init: function() {
- var db, flatten, k, len1, pathname, ref, ref1, ref2, type;
+ var db, flatten, k, len1, pathname, ref, ref1, ref2;
if (location.hostname === 'www.google.com') {
- type = location.pathname === '/recaptcha/api/fallback' ? 'noscript' : 'v2';
- return $.ready(function() {
- return Captcha[type].initFrame();
- });
+ if (location.pathname === '/recaptcha/api/fallback') {
+ $.ready(function() {
+ return Captcha.noscript.initFrame();
+ });
+ } else {
+ $.get('Captcha Fixes', true, function(arg) {
+ var enabled;
+ enabled = arg['Captcha Fixes'];
+ if (enabled) {
+ return $.ready(function() {
+ return Captcha.fixes.init();
+ });
+ }
+ });
+ }
+ return;
}
g.threads = new SimpleDict();
g.posts = new SimpleDict();
@@ -16097,6 +16255,7 @@
case 'a.4cdn.org':
return;
case 'sys.4chan.org':
+ Report.init();
if (g.VIEW === 'post') {
PostSuccessful.init();
}
@@ -17096,10 +17255,8 @@
" /* party hats */\n" +
" pointer-events: none;\n" +
"}\n" +
-"marquee,\n" +
-".postMessage marquee + br,\n" +
-".postMessage marquee + br + br {\n" +
-" display: none;\n" +
+":root:not(.js-enabled) #postForm {\n" +
+" display: table;\n" +
"}\n" +
"/* Anti-autoplay */\n" +
"audio.controls-added {\n" +
diff --git a/builds/4chan-X.zip b/builds/4chan-X.zip
index f3ffaec7d..3890dad90 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 a39527fa2..214b53249 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 a97440e34..06a246d19 100644
--- a/builds/updates.xml
+++ b/builds/updates.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index 46cc83a2c..686d074f5 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -465,198 +465,6 @@
}
}
},
- "grunt-contrib-compress": {
- "version": "0.13.0",
- "resolved": "https://registry.npmjs.org/grunt-contrib-compress/-/grunt-contrib-compress-0.13.0.tgz",
- "dependencies": {
- "archiver": {
- "version": "0.13.1",
- "resolved": "https://registry.npmjs.org/archiver/-/archiver-0.13.1.tgz",
- "dependencies": {
- "async": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz"
- },
- "buffer-crc32": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.5.tgz"
- },
- "glob": {
- "version": "4.3.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-4.3.5.tgz",
- "dependencies": {
- "inflight": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz",
- "dependencies": {
- "wrappy": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
- }
- }
- },
- "inherits": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
- },
- "minimatch": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.1.tgz",
- "dependencies": {
- "brace-expansion": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz",
- "dependencies": {
- "balanced-match": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz"
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
- }
- }
- }
- }
- },
- "once": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/once/-/once-1.3.1.tgz",
- "dependencies": {
- "wrappy": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
- }
- }
- }
- }
- },
- "lazystream": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-0.1.0.tgz"
- },
- "lodash": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz"
- },
- "readable-stream": {
- "version": "1.0.33",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz",
- "dependencies": {
- "core-util-is": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
- },
- "inherits": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
- },
- "isarray": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
- },
- "string_decoder": {
- "version": "0.10.31",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
- }
- }
- },
- "tar-stream": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.1.2.tgz",
- "dependencies": {
- "bl": {
- "version": "0.9.3",
- "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.3.tgz"
- },
- "end-of-stream": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz",
- "dependencies": {
- "once": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/once/-/once-1.3.1.tgz",
- "dependencies": {
- "wrappy": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
- }
- }
- }
- }
- },
- "xtend": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz"
- }
- }
- },
- "zip-stream": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-0.5.0.tgz",
- "dependencies": {
- "compress-commons": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-0.2.0.tgz",
- "dependencies": {
- "crc32-stream": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-0.3.1.tgz"
- },
- "node-int64": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.3.3.tgz"
- }
- }
- }
- }
- }
- }
- },
- "chalk": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
- "dependencies": {
- "ansi-styles": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz"
- },
- "escape-string-regexp": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz"
- },
- "has-ansi": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
- "dependencies": {
- "ansi-regex": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz"
- }
- }
- },
- "strip-ansi": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
- "dependencies": {
- "ansi-regex": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz"
- }
- }
- },
- "supports-color": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz"
- }
- }
- },
- "prettysize": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/prettysize/-/prettysize-0.0.3.tgz"
- }
- }
- },
"grunt-contrib-concat": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-0.5.1.tgz",
@@ -1081,6 +889,16 @@
}
}
},
+ "jszip": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-2.5.0.tgz",
+ "dependencies": {
+ "pako": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.6.tgz"
+ }
+ }
+ },
"load-grunt-tasks": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.1.0.tgz",
diff --git a/package.json b/package.json
index f63b265c9..4b81025e2 100755
--- a/package.json
+++ b/package.json
@@ -3,8 +3,8 @@
"description": "Cross-browser userscript for maximum lurking on 4chan.",
"meta": {
"name": "4chan X",
- "version": "1.10.8.9",
- "date": "2015-04-13T15:07:30.185Z",
+ "version": "1.10.11.1",
+ "date": "2015-04-24T14:48:43.242Z",
"repo": "https://github.com/ccd0/4chan-x/",
"page": "https://github.com/ccd0/4chan-x",
"downloads": "https://ccd0.github.io/4chan-x/builds/",
@@ -22,6 +22,7 @@
"*://a.4cdn.org/*",
"*://i.4cdn.org/*",
"https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*",
+ "https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*",
"*://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc"
],
"suffix": {
@@ -49,7 +50,6 @@
"grunt-concurrent": "^1.0.0",
"grunt-contrib-clean": "^0.6.0",
"grunt-contrib-coffee": "^0.13.0",
- "grunt-contrib-compress": "^0.13.0",
"grunt-contrib-concat": "^0.5.1",
"grunt-contrib-copy": "^0.8.0",
"grunt-contrib-jshint": "^0.11.1",
@@ -57,6 +57,7 @@
"grunt-markdown": "^0.7.0",
"grunt-shell": "^1.1.2",
"grunt-webstore-upload": "^0.8.2",
+ "jszip": "^2.5.0",
"load-grunt-tasks": "^3.1.0",
"npm-shrinkwrap": "^5.3.0"
},
diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee
index 081a3c428..e43889109 100755
--- a/src/Archive/Redirect.coffee
+++ b/src/Archive/Redirect.coffee
@@ -4,15 +4,18 @@ Redirect =
thread: {}
post: {}
file: {}
+ report: {}
archives = {}
for data in Redirect.archives
{name, boards, files, software, withCredentials} = data
archives[name] = data
- for boardID in boards when !withCredentials
- o.thread[boardID] = data unless boardID of o.thread
- o.post[boardID] = data unless boardID of o.post or software isnt 'foolfuuka'
- o.file[boardID] = data unless boardID of o.file or boardID not in files
+ for boardID in boards
+ unless withCredentials
+ o.thread[boardID] = data unless boardID of o.thread
+ o.post[boardID] = data unless boardID of o.post or software isnt 'foolfuuka'
+ o.file[boardID] = data unless boardID of o.file or boardID not in files
+ o.report[boardID] = data if name is 'fgts'
for boardID, record of Conf['selectedArchives']
for type, id of record
@@ -84,6 +87,9 @@ Redirect =
"#{boardID}/?task=search2&search_#{if type is 'image' then 'media_hash' else type}=#{value}"
"#{Redirect.protocol archive}#{archive.domain}/#{path}"
+ report: (archive, {boardID, postID}) ->
+ "https://so.fgts.jp/report/?board=#{boardID}&no=#{postID}"
+
securityCheck: (URL) ->
/^https:\/\//.test(URL) or
location.protocol is 'http:' or
diff --git a/src/Archive/archives.json b/src/Archive/archives.json
index 2e8bd1413..e3b0955dd 100644
--- a/src/Archive/archives.json
+++ b/src/Archive/archives.json
@@ -5,7 +5,7 @@
"http": false,
"https": true,
"software": "foolfuuka",
- "boards": ["a", "biz", "c", "co", "diy", "fit", "gd", "h", "i", "int", "jp", "k", "m", "mlp", "out", "po", "qa", "r9k", "s4s", "sci", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
+ "boards": ["a", "biz", "c", "co", "diy", "fit", "gd", "gif", "h", "i", "int", "jp", "k", "m", "mlp", "out", "po", "qa", "r9k", "s4s", "sci", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
"files": ["a", "biz", "c", "co", "diy", "fit", "gd", "h", "i", "jp", "k", "m", "mlp", "po", "qa", "r9k", "s4s", "sci", "tg", "u", "v", "vg", "vp", "vr", "wsg"]
}, {
"uid": 3,
@@ -32,8 +32,8 @@
"http": true,
"https": true,
"software": "foolfuuka",
- "boards": ["c", "d", "e", "i", "lgbt", "t", "u", "w", "wg"],
- "files": ["c", "d", "e", "i", "lgbt", "t", "u", "w", "wg"]
+ "boards": ["c", "d", "e", "i", "lgbt", "t", "u"],
+ "files": ["c", "d", "e", "i", "lgbt", "t", "u"]
}, {
"uid": 8,
"name": "Rebecca Black Tech",
diff --git a/src/General/Config.coffee b/src/General/Config.coffee
index 0851d933e..e18322dc0 100755
--- a/src/General/Config.coffee
+++ b/src/General/Config.coffee
@@ -35,6 +35,10 @@ Config =
true
'Redirect dead threads and images to the archives.'
]
+ 'Archive Report': [
+ true
+ 'Enable reporting posts to supported archives.'
+ ]
'Except Archives from Encryption': [
false
'Permit loading content from, and warningless redirects to, HTTP-only archives from HTTPS pages.'
@@ -474,6 +478,10 @@ Config =
'Places a link on the bottom of threads to open the QR.'
1
]
+ 'Captcha Fixes': [
+ true
+ 'Make captcha more keyboard-navigable.'
+ ]
'Quote Links':
'Quote Backlinks': [
diff --git a/src/General/Get.coffee b/src/General/Get.coffee
index bfd705f9e..9dffdf722 100755
--- a/src/General/Get.coffee
+++ b/src/General/Get.coffee
@@ -3,7 +3,7 @@ Get =
{OP} = thread
excerpt = "/#{thread.board}/ - " + (
OP.info.subject?.trim() or
- OP.info.comment.replace(/\n+/g, ' // ') or
+ OP.info.commentDisplay.replace(/\n+/g, ' // ') or
OP.info.nameBlock)
return "#{excerpt[...70]}..." if excerpt.length > 73
excerpt
diff --git a/src/General/Main.coffee b/src/General/Main.coffee
index 295f84625..6de834f61 100755
--- a/src/General/Main.coffee
+++ b/src/General/Main.coffee
@@ -1,8 +1,13 @@
Main =
init: ->
if location.hostname is 'www.google.com'
- type = if location.pathname is '/recaptcha/api/fallback' then 'noscript' else 'v2'
- return $.ready -> Captcha[type].initFrame()
+ if location.pathname is '/recaptcha/api/fallback'
+ $.ready -> Captcha.noscript.initFrame()
+ else
+ $.get 'Captcha Fixes', true, ({'Captcha Fixes': enabled}) ->
+ if enabled
+ $.ready -> Captcha.fixes.init()
+ return
g.threads = new SimpleDict()
g.posts = new SimpleDict()
@@ -56,6 +61,7 @@ Main =
when 'a.4cdn.org'
return
when 'sys.4chan.org'
+ Report.init()
PostSuccessful.init() if g.VIEW is 'post'
return
when 'i.4cdn.org'
diff --git a/src/General/css/style.css b/src/General/css/style.css
index f3d1bbc5b..5ed05bffe 100755
--- a/src/General/css/style.css
+++ b/src/General/css/style.css
@@ -105,10 +105,8 @@ hr + div.center:not(.ad-cnt):not(.topad):not(.middlead):not(.bottomad) {
/* party hats */
pointer-events: none;
}
-marquee,
-.postMessage marquee + br,
-.postMessage marquee + br + br {
- display: none;
+:root:not(.js-enabled) #postForm {
+ display: table;
}
/* Anti-autoplay */
@@ -1282,6 +1280,7 @@ input.field.tripped:not(:hover):not(:focus) {
background: linear-gradient(to bottom, #F8F8F8, #DCDCDC) no-repeat;
border: 1px solid #BBB;
border-radius: 2px;
+ height: 100%;
}
#qr-file-button {
width: 15%;
diff --git a/src/General/lib/$.coffee b/src/General/lib/$.coffee
index dc8bdfcc4..04ca0b1e6 100755
--- a/src/General/lib/$.coffee
+++ b/src/General/lib/$.coffee
@@ -125,8 +125,8 @@ $.onExists = (root, selector, subtree, cb) ->
$.addStyle = (css, id, test) ->
style = $.el 'style',
- id: id
textContent: css
+ style.id = id if id?
$.asap (-> d.head and (!test? or test())), ->
$.add d.head, style
style
diff --git a/src/General/lib/post.class b/src/General/lib/post.class
index 2cce8bb31..455d9b7cb 100755
--- a/src/General/lib/post.class
+++ b/src/General/lib/post.class
@@ -106,22 +106,28 @@ class Post
# 'Comment too long'...
# EXIF data. (/p/)
# Rolls. (/tg/)
- # Marquees. (/pol/)
+ # Fortunes. (/s4s/)
+ bq = @nodes.comment.cloneNode true
+ for node in $$ '.abbr + br, .exif, b, .fortune', bq
+ $.rm node
+ if abbr = $ '.abbr', bq
+ $.rm abbr
+ @info.comment = @nodesToText bq
+ if abbr
+ @info.comment = @info.comment.replace /\n\n$/, ''
+
+ # Hide spoilers.
+ # Remove:
# Preceding and following new lines.
# Trailing spaces.
- bq = @nodes.comment.cloneNode true
- for node in $$ '.abbr, .exif, b, marquee', bq
- $.rm node
- @info.comment = @nodesToText bq
-
- # Get the comment's text with spoilers hidden.
- spoilers = $$ 's', bq
- @info.commentSpoilered = if spoilers.length
- for node in spoilers
- $.replace node, $.tn '[spoiler]'
- @nodesToText bq
- else
- @info.comment
+ commentDisplay = @info.comment
+ unless Conf['Remove Spoilers'] or Conf['Reveal Spoilers']
+ spoilers = $$ 's', bq
+ if spoilers.length
+ for node in spoilers
+ $.replace node, $.tn '[spoiler]'
+ commentDisplay = @nodesToText bq
+ @info.commentDisplay = commentDisplay.trim().replace /\s+$/gm, ''
nodesToText: (bq) ->
text = ""
@@ -129,7 +135,7 @@ class Post
i = 0
while node = nodes.snapshotItem i++
text += node.data or '\n'
- text.trim().replace /\s+$/gm, ''
+ text
parseQuotes: ->
@quotes = []
diff --git a/src/Menu/ReportLink.coffee b/src/Menu/ReportLink.coffee
index 73b60a661..23621a317 100755
--- a/src/Menu/ReportLink.coffee
+++ b/src/Menu/ReportLink.coffee
@@ -11,11 +11,14 @@ ReportLink =
el: a
order: 10
open: (post) ->
- ReportLink.post = post
- !post.isDead
+ ReportLink.url = unless post.isDead
+ "//sys.4chan.org/#{post.board}/imgboard.php?mode=report&no=#{post}"
+ else if Conf['Archive Report']
+ Redirect.to 'report', {boardID: post.board.ID, postID: post.ID}
+ !!ReportLink.url
+
report: ->
- {post} = ReportLink
- url = "//sys.4chan.org/#{post.board}/imgboard.php?mode=report&no=#{post}"
+ {url} = ReportLink
id = Date.now()
- set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=285"
+ set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=685,height=200"
window.open url, id, set
diff --git a/src/Miscellaneous/Report.coffee b/src/Miscellaneous/Report.coffee
new file mode 100755
index 000000000..7c76af437
--- /dev/null
+++ b/src/Miscellaneous/Report.coffee
@@ -0,0 +1,34 @@
+Report =
+ init: ->
+ return unless /\bmode=report\b/.test(location.search) and match = location.search.match /\bno=(\d+)/
+ @postID = +match[1]
+ $.ready @ready
+
+ ready: ->
+ new MutationObserver(Report.resize).observe d.body,
+ childList: true
+ attributes: true
+ subtree: true
+ Report.archive() if Conf['Archive Report']
+
+ resize: ->
+ return unless bubble = $ '.gc-bubbleDefault'
+ dy = bubble.getBoundingClientRect().bottom - doc.clientHeight
+ window.resizeBy 0, dy if dy > 0
+
+ archive: ->
+ Redirect.init()
+ return unless url = Redirect.to 'report', {boardID: g.BOARD.ID, postID: Report.postID}
+
+ if (message = $ 'h3') and /Report submitted!/.test(message.textContent)
+ $.globalEval 'self.close = function(){};'
+ window.resizeTo 685, 320
+ location.replace url
+ return
+ link = $.el 'a',
+ href: url
+ textContent: 'Report to fgts'
+ $.on link, 'click', (e) ->
+ unless e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0
+ window.resizeTo 685, 320
+ $.add d.body, [$.tn(' ['), link, $.tn(']')]
diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee
index 74e485977..2934fce49 100755
--- a/src/Monitoring/Unread.coffee
+++ b/src/Monitoring/Unread.coffee
@@ -128,7 +128,7 @@ Unread =
openNotification: (post) ->
return unless Header.areNotificationsEnabled
notif = new Notification "#{post.info.nameBlock} replied to you",
- body: post.info[if Conf['Remove Spoilers'] or Conf['Reveal Spoilers'] then 'comment' else 'commentSpoilered']
+ body: post.info.commentDisplay
icon: Favicon.logo
notif.onclick = ->
Header.scrollToIfNeeded post.nodes.root, true
diff --git a/src/Posting/Captcha.fixes.coffee b/src/Posting/Captcha.fixes.coffee
new file mode 100644
index 000000000..062ab9b5d
--- /dev/null
+++ b/src/Posting/Captcha.fixes.coffee
@@ -0,0 +1,61 @@
+Captcha.fixes =
+ css: '''
+ .rc-imageselect-target > .rc-imageselect-tile > img:focus {
+ outline: 2px solid #4a90e2;
+ }
+ .rc-button-default:focus {
+ box-shadow: inset 0 0 0 2px #0063d6;
+ }
+ '''
+
+ init: ->
+ switch location.pathname.split('/')[3]
+ when 'anchor' then @initMain()
+ when 'frame' then @initPopup()
+
+ initMain: ->
+ $.onExists d.body, '#recaptcha-anchor', true, (checkbox) ->
+ focus = ->
+ if d.hasFocus() and d.activeElement isnt checkbox
+ checkbox.focus()
+ focus()
+ $.on window, 'focus', ->
+ $.queueTask focus
+
+ initPopup: ->
+ $.addStyle @css
+ @fixImages()
+ new MutationObserver(=> @fixImages()).observe d.body, {childList: true, subtree: true}
+ $.on d, 'keydown', @keybinds.bind(@)
+
+ fixImages: ->
+ return unless (@images = $$ '.rc-imageselect-target > .rc-imageselect-tile > img').length
+ focus = @images[0].tabIndex isnt 0
+ for img in @images
+ img.tabIndex = 0
+ @focusImage() if focus
+
+ focusImage: ->
+ # XXX Image is not focusable at first in Firefox; to be refactored when I figure out why.
+ img = @images[0]
+ $.asap ->
+ return true unless doc.contains img
+ img.focus()
+ d.activeElement is img
+ , ->
+
+ keybinds: (e) ->
+ return unless @images and doc.contains(@images[0]) and d.activeElement
+ reload = $.id 'recaptcha-reload-button'
+ verify = $.id 'recaptcha-verify-button'
+ x = @images.indexOf d.activeElement
+ if x < 0
+ return unless $('.rc-controls').contains d.activeElement
+ x = if d.activeElement is verify then 11 else 9
+ return unless dx = {38: 9, 40: 3, 37: 11, 39: 1}[e.keyCode] # Up, Down, Left, Right
+ x = (x + dx) % 12
+ if x is 10
+ x = if dx is 11 then 9 else 11
+ (@images[x] or {9: reload, 11: verify}[x]).focus()
+ e.preventDefault()
+ e.stopPropagation()
diff --git a/src/Posting/Captcha.v2.coffee b/src/Posting/Captcha.v2.coffee
index 1aad5b1d7..3b694940e 100644
--- a/src/Posting/Captcha.v2.coffee
+++ b/src/Posting/Captcha.v2.coffee
@@ -25,13 +25,6 @@ Captcha.v2 =
# XXX Greasemonkey 1.x workaround to gain access to GM_* functions.
$.queueTask => @save false
- initFrame: ->
- $.globalEval 'window.focus = function() {};'
- $.on window, 'focus', ->
- $.queueTask ->
- return unless d.hasFocus() and (checkbox = $.id 'recaptcha-anchor')
- checkbox.focus() unless d.activeElement is checkbox
-
shouldFocus: false
timeouts: {}
postsCount: 0
@@ -125,6 +118,11 @@ Captcha.v2 =
$.rmClass QR.nodes.el, 'captcha-open'
$.rm @nodes.container if @nodes.container
delete @nodes.container
+ # Clean up abandoned iframes.
+ for garbage in $$ 'div > .gc-bubbleDefault'
+ $.rm ins if (ins = garbage.parentNode.nextSibling) and ins.nodeName is 'INS'
+ $.rm garbage.parentNode
+ return
sync: (captchas=[]) ->
@captchas = captchas
@@ -155,11 +153,12 @@ Captcha.v2 =
QR.nodes.status.focus()
@reload()
else
+ focus = d.activeElement?.nodeName is 'IFRAME' and d.activeElement.src?[...38] is 'https://www.google.com/recaptcha/api2/'
if pasted
@destroy()
else
@timeouts.destroy ?= setTimeout @destroy.bind(@), 3 * $.SECOND
- QR.nodes.status.focus()
+ QR.nodes.status.focus() if focus
QR.submit() if Conf['Post on Captcha Completion'] and !QR.cooldown.auto
diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee
index 6e7f759e7..2ef8f144b 100644
--- a/src/Posting/QR.coffee
+++ b/src/Posting/QR.coffee
@@ -9,8 +9,8 @@ QR =
return if g.VIEW is 'archive'
- $.globalEval 'document.documentElement.dataset.jsEnabled = true;'
- noscript = Conf['Force Noscript Captcha'] or !doc.dataset.jsEnabled
+ $.globalEval 'document.documentElement.classList.add("js-enabled");'
+ noscript = Conf['Force Noscript Captcha'] or not $.hasClass doc, 'js-enabled'
@captcha = Captcha[if noscript then 'noscript' else 'v2']
$.on d, '4chanXInitFinished', @initReady
@@ -37,7 +37,7 @@ QR =
if Conf['Hide Original Post Form']
$.addClass doc, 'hide-original-post-form'
- if !doc.dataset.jsEnabled
+ unless $.hasClass doc, 'js-enabled'
# Prevent unnecessary loading of fallback iframe.
$.onExists doc, '#postForm noscript', true, $.rm