diff --git a/CHANGELOG.md b/CHANGELOG.md
index 20d010d51..f98959388 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@
### v1.14.10
+**v1.14.10.1** *(2019-07-19)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.10.1/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.10.1/builds/4chan-X-noupdate.crx)]
+- Merge v1.14.9.2: Fix regression causing thread watcher to stop highlighting active thread.
+- Add option `Filter in Native Catalog` to apply 4chan X filters on native catalog. Also works on vichan sites. Enabled by default for new installs only. #2351
+- (droM4X) Add keybinds to rotate images in Gallery.
+- Other minor bugfixes.
+
**v1.14.10.0** *(2019-07-17)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.10.0/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.10.0/builds/4chan-X-noupdate.crx)]
- Based on v1.14.9.1.
- 4chan X features such as Image Hover, Sauce, Gallery, etc. now work on multiple files per post on vichan sites. #2171
diff --git a/builds/4chan-X-beta.crx b/builds/4chan-X-beta.crx
index 705808d89..c28209d87 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 16106f25f..18edf0ba0 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.14.10.0
+// @version 1.14.10.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
diff --git a/builds/4chan-X-beta.user.js b/builds/4chan-X-beta.user.js
index 9a98b3e25..c54635703 100644
--- a/builds/4chan-X-beta.user.js
+++ b/builds/4chan-X-beta.user.js
@@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X beta
-// @version 1.14.10.0
+// @version 1.14.10.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@@ -184,7 +184,7 @@
'use strict';
-var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, ModContact, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostJumper, PostRedirect, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, SW, Sauce, Settings, ShimSet, SimpleDict, Site, Test, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, Tinyboard, UI, Unread, UnreadIndex, Volume;
+var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, CatalogThreadNative, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, ModContact, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostJumper, PostRedirect, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, SW, Sauce, Settings, ShimSet, SimpleDict, Site, Test, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, Tinyboard, UI, Unread, UnreadIndex, Volume;
var Conf, E, c, d, doc, docSet, g;
@@ -199,7 +199,7 @@ docSet = function() {
};
g = {
- VERSION: '1.14.10.0',
+ VERSION: '1.14.10.1',
NAMESPACE: '4chan X.',
sites: {},
boards: {}
@@ -286,6 +286,7 @@ Config = (function() {
'Anonymize': [false, 'Make everyone Anonymous.'],
'Filter': [true, 'Self-moderation placebo.'],
'Filtered Backlinks': [false, 'When enabled, shows backlinks to filtered posts with a line-through decoration. Otherwise, hides the backlinks.', 1],
+ 'Filter in Native Catalog': [true, 'Apply 4chan X filters in native catalog.', 1],
'Recursive Hiding': [true, 'Hide replies of hidden posts, recursively.'],
'Thread Hiding Buttons': [true, 'Add buttons to hide entire threads.'],
'Reply Hiding Buttons': [true, 'Add buttons to hide single replies.'],
@@ -512,6 +513,8 @@ Config = (function() {
'Advance Gallery': ['Enter', 'Go to next image or, if Autoplay is off, play video.'],
'Pause': ['p', 'Pause/play videos in the gallery.'],
'Slideshow': ['Ctrl+Right', 'Toggle the gallery slideshow mode.'],
+ 'Rotate image clockwise': ['Shift+Right', 'Rotate image clockwise in gallery.'],
+ 'Rotate image anticlockwise': ['Shift+Left', 'Rotate image anticlockwise in gallery.'],
'fappeTyme': ['f', 'Toggle Fappe Tyme.'],
'werkTyme': ['Shift+w', 'Toggle Werk Tyme.'],
'Front page': ['1', 'Jump to front page.'],
@@ -1482,6 +1485,9 @@ body.hasDropDownNav{\n\
#menu a {\n\
margin: 0;\n\
}\n\
+.gal-buttons.gal-buttons a {\n\
+ font-size: inherit;\n\
+}\n\
/* Anti-autoplay */\n\
audio.controls-added {\n\
display: block;\n\
@@ -1675,7 +1681,8 @@ audio.controls-added {\n\
#toggleMsgBtn {\n\
display: none !important;\n\
}\n\
-#board-list .current {\n\
+.current,\n\
+:root.sw-yotsuba div#boardNavDesktopFoot a.current {\n\
font-weight: bold;\n\
}\n\
@media (min-width: 1300px) {\n\
@@ -2759,7 +2766,8 @@ input[name=\"Default Volume\"] {\n\
:root:not(.werkTyme) .catalog-thread.filter-highlight .catalog-thumb,\n\
:root.werkTyme .catalog-thread.filter-highlight:not(:hover),\n\
:root.werkTyme:not(.catalog-hover-expand) .catalog-thread.filter-highlight,\n\
-:root.werkTyme.catalog-hover-expand .catalog-thread.filter-highlight > .catalog-container:hover > .catalog-post {\n\
+:root.werkTyme.catalog-hover-expand .catalog-thread.filter-highlight > .catalog-container:hover > .catalog-post,\n\
+:root.catalog $site$catalog$thread.filter-highlight$site$relative$catalogHighlight {\n\
box-shadow: 0 0 3px 3px rgba(255, 0, 0, .5);\n\
}\n\
:root:not(.werkTyme) .catalog-thread.watched .catalog-thumb,\n\
@@ -3559,6 +3567,8 @@ a:only-of-type > .remove {\n\
overflow-x: scroll !important;\n\
}\n\
.gal-image a {\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
margin: auto;\n\
line-height: 0;\n\
max-width: 100%;\n\
@@ -5808,6 +5818,8 @@ Callbacks = (function() {
Callbacks.CatalogThread = new Callbacks('Catalog Thread');
+ Callbacks.CatalogThreadNative = new Callbacks('Catalog Thread');
+
function Callbacks(type) {
this.type = type;
this.keys = [];
@@ -5896,6 +5908,34 @@ CatalogThread = (function() {
}).call(this);
+CatalogThreadNative = (function() {
+ var CatalogThreadNative;
+
+ CatalogThreadNative = (function() {
+ CatalogThreadNative.prototype.toString = function() {
+ return this.ID;
+ };
+
+ function CatalogThreadNative(root) {
+ this.nodes = {
+ root: root,
+ thumb: $(g.SITE.selectors.catalog.thumb, root)
+ };
+ this.siteID = g.SITE.ID;
+ this.boardID = this.nodes.thumb.parentNode.pathname.split(/\/+/)[1];
+ this.board = g.boards[this.boardID] || new Board(this.boardID);
+ this.ID = this.threadID = +(root.dataset.id || root.id).match(/\d*$/)[0];
+ this.thread = this.board.threads[this.ID] || new Thread(this.ID, this.board);
+ }
+
+ return CatalogThreadNative;
+
+ })();
+
+ return CatalogThreadNative;
+
+}).call(this);
+
Connection = (function() {
var Connection,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -7695,11 +7735,17 @@ SW = {};
relative: {
opHighlight: ' > .op',
replyPost: '.reply',
- replyOriginal: 'div[id^="reply_"]:not(.hidden)'
+ replyOriginal: 'div[id^="reply_"]:not(.hidden)',
+ catalogHighlight: ' > .thread'
},
comment: '.body',
spoiler: '.spoiler',
quotelink: 'a[onclick^="highlightReply("]',
+ catalog: {
+ board: '#Grid',
+ thread: '.mix',
+ thumb: '.thread-image'
+ },
boardList: '.boardlist',
boardListBottom: '.boardlist.bottom',
styleSheet: '#stylesheet',
@@ -7803,6 +7849,9 @@ SW = {};
},
isLinkified: function(link) {
return /\bnofollow\b/.test(link.rel);
+ },
+ catalogPin: function(threadRoot) {
+ return threadRoot.dataset.sticky = 'true';
}
};
@@ -7909,11 +7958,17 @@ SW = {};
relative: {
opHighlight: '.opContainer',
replyPost: ' > .reply',
- replyOriginal: '.replyContainer:not([data-clone])'
+ replyOriginal: '.replyContainer:not([data-clone])',
+ catalogHighlight: ''
},
comment: '.postMessage',
spoiler: 's',
quotelink: ':not(pre) > .quotelink',
+ catalog: {
+ board: '#threads',
+ thread: '.thread',
+ thumb: '.thumb'
+ },
boardList: '#boardNavDesktop > .boardList',
boardListBottom: '#boardNavDesktopFoot > .boardList',
styleSheet: 'link[title=switch]',
@@ -8813,7 +8868,10 @@ Filter = (function() {
results: {},
init: function() {
var base, base1, boards, err, excludes, file, filter, hide, hl, i, isstring, j, key, len, len1, line, mask, noti, op, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, regexp, stub, top, type, types;
- if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Filter'])) {
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'catalog') && Conf['Filter'])) {
+ return;
+ }
+ if (g.VIEW === 'catalog' && !Conf['Filter in Native Catalog']) {
return;
}
if (!Conf['Filtered Backlinks']) {
@@ -8903,10 +8961,14 @@ Filter = (function() {
if (!Object.keys(this.filters).length) {
return;
}
- return Callbacks.Post.push({
- name: 'Filter',
- cb: this.node
- });
+ if (g.VIEW === 'catalog') {
+ return Filter.catalog();
+ } else {
+ return Callbacks.Post.push({
+ name: 'Filter',
+ cb: this.node
+ });
+ }
},
parseBoards: function(boardsRaw) {
var boardID, boardID2, boards, i, j, len, len1, ref, ref1, ref2, ref3, site, siteFilter, siteID;
@@ -9026,6 +9088,68 @@ Filter = (function() {
return Unread.openNotification(this, ' triggered a notification filter');
}
},
+ catalog: function() {
+ var base, url;
+ if (!(url = typeof (base = g.SITE.urls).catalogJSON === "function" ? base.catalogJSON(g.BOARD) : void 0)) {
+ return;
+ }
+ Filter.catalogData = {};
+ $.ajax(url, {
+ onloadend: Filter.catalogParse
+ });
+ return Callbacks.CatalogThreadNative.push({
+ name: 'Filter',
+ cb: this.catalogNode
+ });
+ },
+ catalogParse: function() {
+ var i, item, j, len, len1, page, ref, ref1, ref2;
+ if ((ref = this.status) !== 200 && ref !== 404) {
+ new Notice('warning', "Failed to fetch catalog JSON data. " + (this.status ? "Error " + this.statusText + " (" + this.status + ")" : 'Connection Error'), 1);
+ return;
+ }
+ ref1 = this.response;
+ for (i = 0, len = ref1.length; i < len; i++) {
+ page = ref1[i];
+ ref2 = page.threads;
+ for (j = 0, len1 = ref2.length; j < len1; j++) {
+ item = ref2[j];
+ Filter.catalogData[item.no] = item;
+ }
+ }
+ g.BOARD.threads.forEach(function(thread) {
+ if (thread.catalogViewNative) {
+ return Filter.catalogNode.call(thread.catalogViewNative);
+ }
+ });
+ },
+ catalogNode: function() {
+ var base, hide, hl, ref, ref1, top;
+ if (!(this.boardID === g.BOARD.ID && Filter.catalogData[this.ID])) {
+ return;
+ }
+ if ((ref = QuoteYou.db) != null ? ref.get({
+ siteID: g.SITE.ID,
+ boardID: this.boardID,
+ threadID: this.ID,
+ postID: this.ID
+ }) : void 0) {
+ return;
+ }
+ ref1 = Filter.test(g.SITE.Build.parseJSON(Filter.catalogData[this.ID], this)), hide = ref1.hide, hl = ref1.hl, top = ref1.top;
+ if (hide) {
+ return this.nodes.root.hidden = true;
+ } else {
+ if (hl) {
+ this.highlights = hl;
+ $.addClass.apply($, [this.nodes.root].concat(slice.call(hl)));
+ }
+ if (top) {
+ $.prepend(this.nodes.root.parentNode, this.nodes.root);
+ return typeof (base = g.SITE).catalogPin === "function" ? base.catalogPin(this.nodes.root) : void 0;
+ }
+ }
+ },
isHidden: function(post) {
return !!Filter.test(post).hide;
},
@@ -12915,6 +13039,11 @@ Settings = (function() {
set('fourchanImageHost', (data['Use Faster Image Host'] ? 'i.4cdn.org' : ''));
}
}
+ if (compareString < '00001.00014.00010.00001') {
+ if (data['Filter in Native Catalog'] == null) {
+ set('Filter in Native Catalog', false);
+ }
+ }
return changes;
},
loadSettings: function(data, cb) {
@@ -12975,7 +13104,7 @@ Settings = (function() {
};
});
$.extend(div, {
- innerHTML: "
Filter is disabled.
Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 and Unique ID filtering use exact string matching, not regular expressions.
You can use these settings with each regular expression, separate them with semicolons:- Per boards, separate them with commas. It is global if not specified. Use
sfw and nsfw to reference all worksafe or not-worksafe boards.
For example: boards:a,jp;.
To specify boards on a particular site, put the beginning of the domain and a slash character before the list.
Any initial www. should not be included, and all 4chan domains are considered 4chan.org.
For example: boards:4:a,jp,sama:a,z;.
An asterisk can be used to specify all boards on a site.
For example: boards:4:*;.
- Select boards to be excluded from the filter. The syntax is the same as for the
boards: option above.
For example: exclude:vg,v;. - Filter OPs only along with their threads (`only`) or replies only (`no`).
For example: op:only; or op:no;. - Filter only posts with files (`only`) or only posts without files (`no`).
For example: file:only; or file:no;. - Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
For example: stub:yes; or stub:no;. - Highlight instead of hiding. You can specify a class name to use with a userstyle.
For example: highlight; or highlight:wallpaper;. - Highlighted OPs will have their threads put on top of the board index by default.
For example: top:yes; or top:no;. - Show a desktop notification instead of hiding.
For example: notify;. - Filters in the \"General\" section apply to multiple fields, by default
subject,name,filename,comment.
The fields can be specified with the type option, separated by commas.
For example: type:" + E.cat(filterTypes) + ";.
Types can also be combined with a + sign; this indicates the filter applies to the given fields joined by newlines.
For example: type:filename+filesize+dimensions;.
Note: If you're using the native catalog rather than 4chan X's catalog, 4chan X's filters do not apply there.
The native catalog has its own separate filter list.
"
+ innerHTML: "Filter is disabled.
Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 and Unique ID filtering use exact string matching, not regular expressions.
You can use these settings with each regular expression, separate them with semicolons:- Per boards, separate them with commas. It is global if not specified. Use
sfw and nsfw to reference all worksafe or not-worksafe boards.
For example: boards:a,jp;.
To specify boards on a particular site, put the beginning of the domain and a slash character before the list.
Any initial www. should not be included, and all 4chan domains are considered 4chan.org.
For example: boards:4:a,jp,sama:a,z;.
An asterisk can be used to specify all boards on a site.
For example: boards:4:*;.
- Select boards to be excluded from the filter. The syntax is the same as for the
boards: option above.
For example: exclude:vg,v;. - Filter OPs only along with their threads (`only`) or replies only (`no`).
For example: op:only; or op:no;. - Filter only posts with files (`only`) or only posts without files (`no`).
For example: file:only; or file:no;. - Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
For example: stub:yes; or stub:no;. - Highlight instead of hiding. You can specify a class name to use with a userstyle.
For example: highlight; or highlight:wallpaper;. - Highlighted OPs will have their threads put on top of the board index by default.
For example: top:yes; or top:no;. - Show a desktop notification instead of hiding.
For example: notify;. - Filters in the \"General\" section apply to multiple fields, by default
subject,name,filename,comment.
The fields can be specified with the type option, separated by commas.
For example: type:" + E.cat(filterTypes) + ";.
Types can also be combined with a + sign; this indicates the filter applies to the given fields joined by newlines.
For example: type:filename+filesize+dimensions;.
"
});
return $('.warning', div).hidden = Conf['Filter'];
},
@@ -14253,6 +14382,10 @@ Gallery = (function() {
return Gallery.cb.pause;
case Conf['Slideshow']:
return Gallery.cb.toggleSlideshow;
+ case Conf['Rotate image anticlockwise']:
+ return Gallery.cb.rotateLeft;
+ case Conf['Rotate image clockwise']:
+ return Gallery.cb.rotateRight;
}
})();
if (!cb) {
@@ -14332,6 +14465,22 @@ Gallery = (function() {
$.rmClass(Gallery.nodes.buttons, 'gal-playing');
return Gallery.slideshow = false;
},
+ rotateLeft: function() {
+ return Gallery.cb.rotate(270);
+ },
+ rotateRight: function() {
+ return Gallery.cb.rotate(90);
+ },
+ rotate: $.debounce(100, function(delta) {
+ var current;
+ current = Gallery.nodes.current;
+ if (current.nodeName === 'IFRAME') {
+ return;
+ }
+ current.dataRotate = ((current.dataRotate || 0) + delta) % 360;
+ current.style.transform = "rotate(" + current.dataRotate + "deg)";
+ return Gallery.cb.setHeight();
+ }),
close: function() {
$.off(Gallery.nodes.current, 'error', Gallery.error);
ImageCommon.pause(Gallery.nodes.current);
@@ -14360,16 +14509,29 @@ Gallery = (function() {
return (this.checked ? $.addClass : $.rmClass)(doc, "gal-" + (this.name.toLowerCase().replace(/\s+/g, '-')));
},
setHeight: $.debounce(100, function() {
- var current, dim, frame, height, minHeight, ref, ref1, ref2, style, width;
+ var containerHeight, containerWidth, current, dim, frame, height, margin, minHeight, ref, ref1, ref2, ref3, style, width;
ref = Gallery.nodes, current = ref.current, frame = ref.frame;
style = current.style;
if (Conf['Stretch to Fit'] && (dim = (ref1 = g.posts[current.dataset.post]) != null ? ref1.file.dimensions : void 0)) {
ref2 = dim.split('x'), width = ref2[0], height = ref2[1];
- minHeight = Math.min(doc.clientHeight - 25, height / width * frame.clientWidth);
+ containerWidth = frame.clientWidth;
+ containerHeight = doc.clientHeight - 25;
+ if ((current.dataRotate || 0) % 180 === 90) {
+ ref3 = [containerHeight, containerWidth], containerWidth = ref3[0], containerHeight = ref3[1];
+ }
+ minHeight = Math.min(containerHeight, height / width * containerWidth);
style.minHeight = minHeight + 'px';
- return style.minWidth = (width / height * minHeight) + 'px';
+ style.minWidth = (width / height * minHeight) + 'px';
} else {
- return style.minHeight = style.minWidth = '';
+ style.minHeight = style.minWidth = '';
+ }
+ if ((current.dataRotate || 0) % 180 === 90) {
+ style.maxWidth = Conf['Fit Height'] ? (doc.clientHeight - 25) + "px" : 'none';
+ style.maxHeight = Conf['Fit Width'] ? frame.clientWidth + "px" : 'none';
+ margin = (current.clientWidth - current.clientHeight) / 2;
+ return style.margin = margin + "px " + (-margin) + "px";
+ } else {
+ return style.maxWidth = style.maxHeight = style.margin = '';
}
}),
setDelay: function() {
@@ -27043,7 +27205,9 @@ Main = (function() {
});
new Notice('warning', msg);
}
- if (!Index.enabled) {
+ if (g.VIEW === 'catalog') {
+ return Main.initCatalog();
+ } else if (!Index.enabled) {
return Main.initThread();
} else {
Main.expectInitFinished = true;
@@ -27205,6 +27369,68 @@ Main = (function() {
}
});
},
+ initCatalog: function() {
+ var board, errors, s, threads;
+ s = g.SITE.selectors.catalog;
+ if (s && (board = $(s.board))) {
+ threads = [];
+ errors = [];
+ Main.addCatalogThreadsObserver = new MutationObserver(Main.addCatalogThreads);
+ Main.addCatalogThreadsObserver.observe(board, {
+ childList: true
+ });
+ Main.parseCatalogThreads($$(s.thread, board), threads, errors);
+ if (errors.length) {
+ Main.handleErrors(errors);
+ }
+ Main.callbackNodes('CatalogThreadNative', threads);
+ }
+ Main.expectInitFinished = true;
+ return $.event('4chanXInitFinished');
+ },
+ parseCatalogThreads: function(threadRoots, threads, errors) {
+ var err, j, len, ref, thread, threadRoot;
+ for (j = 0, len = threadRoots.length; j < len; j++) {
+ threadRoot = threadRoots[j];
+ try {
+ thread = new CatalogThreadNative(threadRoot);
+ if (((ref = thread.thread.catalogViewNative) != null ? ref.nodes.root : void 0) !== threadRoot) {
+ thread.thread.catalogViewNative = thread;
+ threads.push(thread);
+ }
+ } catch (error1) {
+ err = error1;
+ errors.push({
+ message: "Parsing of Catalog Thread No." + ((threadRoot.dataset.id || threadRoot.id).match(/\d+/)) + " failed. Thread will be skipped.",
+ error: err
+ });
+ }
+ }
+ },
+ addCatalogThreads: function(records) {
+ var errors, j, k, len, len1, node, record, ref, threadRoots, threads;
+ threadRoots = [];
+ for (j = 0, len = records.length; j < len; j++) {
+ record = records[j];
+ ref = record.addedNodes;
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ node = ref[k];
+ if (node.nodeType === Node.ELEMENT_NODE && node.matches(g.SITE.selectors.catalog.thread)) {
+ threadRoots.push(node);
+ }
+ }
+ }
+ if (!threadRoots.length) {
+ return;
+ }
+ threads = [];
+ errors = [];
+ Main.parseCatalogThreads(threadRoots, threads, errors);
+ if (errors.length) {
+ Main.handleErrors(errors);
+ }
+ return Main.callbackNodes('CatalogThreadNative', threads);
+ },
callbackNodes: function(klass, nodes) {
var cb, i, node;
i = 0;
diff --git a/builds/4chan-X-noupdate.crx b/builds/4chan-X-noupdate.crx
index 3f7366513..351c04719 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 eb55b2858..90758dbc5 100644
--- a/builds/4chan-X-noupdate.user.js
+++ b/builds/4chan-X-noupdate.user.js
@@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X
-// @version 1.14.10.0
+// @version 1.14.10.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@@ -184,7 +184,7 @@
'use strict';
-var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, ModContact, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostJumper, PostRedirect, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, SW, Sauce, Settings, ShimSet, SimpleDict, Site, Test, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, Tinyboard, UI, Unread, UnreadIndex, Volume;
+var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, CatalogThreadNative, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, ModContact, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostJumper, PostRedirect, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, SW, Sauce, Settings, ShimSet, SimpleDict, Site, Test, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, Tinyboard, UI, Unread, UnreadIndex, Volume;
var Conf, E, c, d, doc, docSet, g;
@@ -199,7 +199,7 @@ docSet = function() {
};
g = {
- VERSION: '1.14.10.0',
+ VERSION: '1.14.10.1',
NAMESPACE: '4chan X.',
sites: {},
boards: {}
@@ -286,6 +286,7 @@ Config = (function() {
'Anonymize': [false, 'Make everyone Anonymous.'],
'Filter': [true, 'Self-moderation placebo.'],
'Filtered Backlinks': [false, 'When enabled, shows backlinks to filtered posts with a line-through decoration. Otherwise, hides the backlinks.', 1],
+ 'Filter in Native Catalog': [true, 'Apply 4chan X filters in native catalog.', 1],
'Recursive Hiding': [true, 'Hide replies of hidden posts, recursively.'],
'Thread Hiding Buttons': [true, 'Add buttons to hide entire threads.'],
'Reply Hiding Buttons': [true, 'Add buttons to hide single replies.'],
@@ -512,6 +513,8 @@ Config = (function() {
'Advance Gallery': ['Enter', 'Go to next image or, if Autoplay is off, play video.'],
'Pause': ['p', 'Pause/play videos in the gallery.'],
'Slideshow': ['Ctrl+Right', 'Toggle the gallery slideshow mode.'],
+ 'Rotate image clockwise': ['Shift+Right', 'Rotate image clockwise in gallery.'],
+ 'Rotate image anticlockwise': ['Shift+Left', 'Rotate image anticlockwise in gallery.'],
'fappeTyme': ['f', 'Toggle Fappe Tyme.'],
'werkTyme': ['Shift+w', 'Toggle Werk Tyme.'],
'Front page': ['1', 'Jump to front page.'],
@@ -1482,6 +1485,9 @@ body.hasDropDownNav{\n\
#menu a {\n\
margin: 0;\n\
}\n\
+.gal-buttons.gal-buttons a {\n\
+ font-size: inherit;\n\
+}\n\
/* Anti-autoplay */\n\
audio.controls-added {\n\
display: block;\n\
@@ -1675,7 +1681,8 @@ audio.controls-added {\n\
#toggleMsgBtn {\n\
display: none !important;\n\
}\n\
-#board-list .current {\n\
+.current,\n\
+:root.sw-yotsuba div#boardNavDesktopFoot a.current {\n\
font-weight: bold;\n\
}\n\
@media (min-width: 1300px) {\n\
@@ -2759,7 +2766,8 @@ input[name=\"Default Volume\"] {\n\
:root:not(.werkTyme) .catalog-thread.filter-highlight .catalog-thumb,\n\
:root.werkTyme .catalog-thread.filter-highlight:not(:hover),\n\
:root.werkTyme:not(.catalog-hover-expand) .catalog-thread.filter-highlight,\n\
-:root.werkTyme.catalog-hover-expand .catalog-thread.filter-highlight > .catalog-container:hover > .catalog-post {\n\
+:root.werkTyme.catalog-hover-expand .catalog-thread.filter-highlight > .catalog-container:hover > .catalog-post,\n\
+:root.catalog $site$catalog$thread.filter-highlight$site$relative$catalogHighlight {\n\
box-shadow: 0 0 3px 3px rgba(255, 0, 0, .5);\n\
}\n\
:root:not(.werkTyme) .catalog-thread.watched .catalog-thumb,\n\
@@ -3559,6 +3567,8 @@ a:only-of-type > .remove {\n\
overflow-x: scroll !important;\n\
}\n\
.gal-image a {\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
margin: auto;\n\
line-height: 0;\n\
max-width: 100%;\n\
@@ -5808,6 +5818,8 @@ Callbacks = (function() {
Callbacks.CatalogThread = new Callbacks('Catalog Thread');
+ Callbacks.CatalogThreadNative = new Callbacks('Catalog Thread');
+
function Callbacks(type) {
this.type = type;
this.keys = [];
@@ -5896,6 +5908,34 @@ CatalogThread = (function() {
}).call(this);
+CatalogThreadNative = (function() {
+ var CatalogThreadNative;
+
+ CatalogThreadNative = (function() {
+ CatalogThreadNative.prototype.toString = function() {
+ return this.ID;
+ };
+
+ function CatalogThreadNative(root) {
+ this.nodes = {
+ root: root,
+ thumb: $(g.SITE.selectors.catalog.thumb, root)
+ };
+ this.siteID = g.SITE.ID;
+ this.boardID = this.nodes.thumb.parentNode.pathname.split(/\/+/)[1];
+ this.board = g.boards[this.boardID] || new Board(this.boardID);
+ this.ID = this.threadID = +(root.dataset.id || root.id).match(/\d*$/)[0];
+ this.thread = this.board.threads[this.ID] || new Thread(this.ID, this.board);
+ }
+
+ return CatalogThreadNative;
+
+ })();
+
+ return CatalogThreadNative;
+
+}).call(this);
+
Connection = (function() {
var Connection,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -7695,11 +7735,17 @@ SW = {};
relative: {
opHighlight: ' > .op',
replyPost: '.reply',
- replyOriginal: 'div[id^="reply_"]:not(.hidden)'
+ replyOriginal: 'div[id^="reply_"]:not(.hidden)',
+ catalogHighlight: ' > .thread'
},
comment: '.body',
spoiler: '.spoiler',
quotelink: 'a[onclick^="highlightReply("]',
+ catalog: {
+ board: '#Grid',
+ thread: '.mix',
+ thumb: '.thread-image'
+ },
boardList: '.boardlist',
boardListBottom: '.boardlist.bottom',
styleSheet: '#stylesheet',
@@ -7803,6 +7849,9 @@ SW = {};
},
isLinkified: function(link) {
return /\bnofollow\b/.test(link.rel);
+ },
+ catalogPin: function(threadRoot) {
+ return threadRoot.dataset.sticky = 'true';
}
};
@@ -7909,11 +7958,17 @@ SW = {};
relative: {
opHighlight: '.opContainer',
replyPost: ' > .reply',
- replyOriginal: '.replyContainer:not([data-clone])'
+ replyOriginal: '.replyContainer:not([data-clone])',
+ catalogHighlight: ''
},
comment: '.postMessage',
spoiler: 's',
quotelink: ':not(pre) > .quotelink',
+ catalog: {
+ board: '#threads',
+ thread: '.thread',
+ thumb: '.thumb'
+ },
boardList: '#boardNavDesktop > .boardList',
boardListBottom: '#boardNavDesktopFoot > .boardList',
styleSheet: 'link[title=switch]',
@@ -8813,7 +8868,10 @@ Filter = (function() {
results: {},
init: function() {
var base, base1, boards, err, excludes, file, filter, hide, hl, i, isstring, j, key, len, len1, line, mask, noti, op, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, regexp, stub, top, type, types;
- if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Filter'])) {
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'catalog') && Conf['Filter'])) {
+ return;
+ }
+ if (g.VIEW === 'catalog' && !Conf['Filter in Native Catalog']) {
return;
}
if (!Conf['Filtered Backlinks']) {
@@ -8903,10 +8961,14 @@ Filter = (function() {
if (!Object.keys(this.filters).length) {
return;
}
- return Callbacks.Post.push({
- name: 'Filter',
- cb: this.node
- });
+ if (g.VIEW === 'catalog') {
+ return Filter.catalog();
+ } else {
+ return Callbacks.Post.push({
+ name: 'Filter',
+ cb: this.node
+ });
+ }
},
parseBoards: function(boardsRaw) {
var boardID, boardID2, boards, i, j, len, len1, ref, ref1, ref2, ref3, site, siteFilter, siteID;
@@ -9026,6 +9088,68 @@ Filter = (function() {
return Unread.openNotification(this, ' triggered a notification filter');
}
},
+ catalog: function() {
+ var base, url;
+ if (!(url = typeof (base = g.SITE.urls).catalogJSON === "function" ? base.catalogJSON(g.BOARD) : void 0)) {
+ return;
+ }
+ Filter.catalogData = {};
+ $.ajax(url, {
+ onloadend: Filter.catalogParse
+ });
+ return Callbacks.CatalogThreadNative.push({
+ name: 'Filter',
+ cb: this.catalogNode
+ });
+ },
+ catalogParse: function() {
+ var i, item, j, len, len1, page, ref, ref1, ref2;
+ if ((ref = this.status) !== 200 && ref !== 404) {
+ new Notice('warning', "Failed to fetch catalog JSON data. " + (this.status ? "Error " + this.statusText + " (" + this.status + ")" : 'Connection Error'), 1);
+ return;
+ }
+ ref1 = this.response;
+ for (i = 0, len = ref1.length; i < len; i++) {
+ page = ref1[i];
+ ref2 = page.threads;
+ for (j = 0, len1 = ref2.length; j < len1; j++) {
+ item = ref2[j];
+ Filter.catalogData[item.no] = item;
+ }
+ }
+ g.BOARD.threads.forEach(function(thread) {
+ if (thread.catalogViewNative) {
+ return Filter.catalogNode.call(thread.catalogViewNative);
+ }
+ });
+ },
+ catalogNode: function() {
+ var base, hide, hl, ref, ref1, top;
+ if (!(this.boardID === g.BOARD.ID && Filter.catalogData[this.ID])) {
+ return;
+ }
+ if ((ref = QuoteYou.db) != null ? ref.get({
+ siteID: g.SITE.ID,
+ boardID: this.boardID,
+ threadID: this.ID,
+ postID: this.ID
+ }) : void 0) {
+ return;
+ }
+ ref1 = Filter.test(g.SITE.Build.parseJSON(Filter.catalogData[this.ID], this)), hide = ref1.hide, hl = ref1.hl, top = ref1.top;
+ if (hide) {
+ return this.nodes.root.hidden = true;
+ } else {
+ if (hl) {
+ this.highlights = hl;
+ $.addClass.apply($, [this.nodes.root].concat(slice.call(hl)));
+ }
+ if (top) {
+ $.prepend(this.nodes.root.parentNode, this.nodes.root);
+ return typeof (base = g.SITE).catalogPin === "function" ? base.catalogPin(this.nodes.root) : void 0;
+ }
+ }
+ },
isHidden: function(post) {
return !!Filter.test(post).hide;
},
@@ -12915,6 +13039,11 @@ Settings = (function() {
set('fourchanImageHost', (data['Use Faster Image Host'] ? 'i.4cdn.org' : ''));
}
}
+ if (compareString < '00001.00014.00010.00001') {
+ if (data['Filter in Native Catalog'] == null) {
+ set('Filter in Native Catalog', false);
+ }
+ }
return changes;
},
loadSettings: function(data, cb) {
@@ -12975,7 +13104,7 @@ Settings = (function() {
};
});
$.extend(div, {
- innerHTML: "Filter is disabled.
Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 and Unique ID filtering use exact string matching, not regular expressions.
You can use these settings with each regular expression, separate them with semicolons:- Per boards, separate them with commas. It is global if not specified. Use
sfw and nsfw to reference all worksafe or not-worksafe boards.
For example: boards:a,jp;.
To specify boards on a particular site, put the beginning of the domain and a slash character before the list.
Any initial www. should not be included, and all 4chan domains are considered 4chan.org.
For example: boards:4:a,jp,sama:a,z;.
An asterisk can be used to specify all boards on a site.
For example: boards:4:*;.
- Select boards to be excluded from the filter. The syntax is the same as for the
boards: option above.
For example: exclude:vg,v;. - Filter OPs only along with their threads (`only`) or replies only (`no`).
For example: op:only; or op:no;. - Filter only posts with files (`only`) or only posts without files (`no`).
For example: file:only; or file:no;. - Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
For example: stub:yes; or stub:no;. - Highlight instead of hiding. You can specify a class name to use with a userstyle.
For example: highlight; or highlight:wallpaper;. - Highlighted OPs will have their threads put on top of the board index by default.
For example: top:yes; or top:no;. - Show a desktop notification instead of hiding.
For example: notify;. - Filters in the \"General\" section apply to multiple fields, by default
subject,name,filename,comment.
The fields can be specified with the type option, separated by commas.
For example: type:" + E.cat(filterTypes) + ";.
Types can also be combined with a + sign; this indicates the filter applies to the given fields joined by newlines.
For example: type:filename+filesize+dimensions;.
Note: If you're using the native catalog rather than 4chan X's catalog, 4chan X's filters do not apply there.
The native catalog has its own separate filter list.
"
+ innerHTML: "Filter is disabled.
Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 and Unique ID filtering use exact string matching, not regular expressions.
You can use these settings with each regular expression, separate them with semicolons:- Per boards, separate them with commas. It is global if not specified. Use
sfw and nsfw to reference all worksafe or not-worksafe boards.
For example: boards:a,jp;.
To specify boards on a particular site, put the beginning of the domain and a slash character before the list.
Any initial www. should not be included, and all 4chan domains are considered 4chan.org.
For example: boards:4:a,jp,sama:a,z;.
An asterisk can be used to specify all boards on a site.
For example: boards:4:*;.
- Select boards to be excluded from the filter. The syntax is the same as for the
boards: option above.
For example: exclude:vg,v;. - Filter OPs only along with their threads (`only`) or replies only (`no`).
For example: op:only; or op:no;. - Filter only posts with files (`only`) or only posts without files (`no`).
For example: file:only; or file:no;. - Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
For example: stub:yes; or stub:no;. - Highlight instead of hiding. You can specify a class name to use with a userstyle.
For example: highlight; or highlight:wallpaper;. - Highlighted OPs will have their threads put on top of the board index by default.
For example: top:yes; or top:no;. - Show a desktop notification instead of hiding.
For example: notify;. - Filters in the \"General\" section apply to multiple fields, by default
subject,name,filename,comment.
The fields can be specified with the type option, separated by commas.
For example: type:" + E.cat(filterTypes) + ";.
Types can also be combined with a + sign; this indicates the filter applies to the given fields joined by newlines.
For example: type:filename+filesize+dimensions;.
"
});
return $('.warning', div).hidden = Conf['Filter'];
},
@@ -14253,6 +14382,10 @@ Gallery = (function() {
return Gallery.cb.pause;
case Conf['Slideshow']:
return Gallery.cb.toggleSlideshow;
+ case Conf['Rotate image anticlockwise']:
+ return Gallery.cb.rotateLeft;
+ case Conf['Rotate image clockwise']:
+ return Gallery.cb.rotateRight;
}
})();
if (!cb) {
@@ -14332,6 +14465,22 @@ Gallery = (function() {
$.rmClass(Gallery.nodes.buttons, 'gal-playing');
return Gallery.slideshow = false;
},
+ rotateLeft: function() {
+ return Gallery.cb.rotate(270);
+ },
+ rotateRight: function() {
+ return Gallery.cb.rotate(90);
+ },
+ rotate: $.debounce(100, function(delta) {
+ var current;
+ current = Gallery.nodes.current;
+ if (current.nodeName === 'IFRAME') {
+ return;
+ }
+ current.dataRotate = ((current.dataRotate || 0) + delta) % 360;
+ current.style.transform = "rotate(" + current.dataRotate + "deg)";
+ return Gallery.cb.setHeight();
+ }),
close: function() {
$.off(Gallery.nodes.current, 'error', Gallery.error);
ImageCommon.pause(Gallery.nodes.current);
@@ -14360,16 +14509,29 @@ Gallery = (function() {
return (this.checked ? $.addClass : $.rmClass)(doc, "gal-" + (this.name.toLowerCase().replace(/\s+/g, '-')));
},
setHeight: $.debounce(100, function() {
- var current, dim, frame, height, minHeight, ref, ref1, ref2, style, width;
+ var containerHeight, containerWidth, current, dim, frame, height, margin, minHeight, ref, ref1, ref2, ref3, style, width;
ref = Gallery.nodes, current = ref.current, frame = ref.frame;
style = current.style;
if (Conf['Stretch to Fit'] && (dim = (ref1 = g.posts[current.dataset.post]) != null ? ref1.file.dimensions : void 0)) {
ref2 = dim.split('x'), width = ref2[0], height = ref2[1];
- minHeight = Math.min(doc.clientHeight - 25, height / width * frame.clientWidth);
+ containerWidth = frame.clientWidth;
+ containerHeight = doc.clientHeight - 25;
+ if ((current.dataRotate || 0) % 180 === 90) {
+ ref3 = [containerHeight, containerWidth], containerWidth = ref3[0], containerHeight = ref3[1];
+ }
+ minHeight = Math.min(containerHeight, height / width * containerWidth);
style.minHeight = minHeight + 'px';
- return style.minWidth = (width / height * minHeight) + 'px';
+ style.minWidth = (width / height * minHeight) + 'px';
} else {
- return style.minHeight = style.minWidth = '';
+ style.minHeight = style.minWidth = '';
+ }
+ if ((current.dataRotate || 0) % 180 === 90) {
+ style.maxWidth = Conf['Fit Height'] ? (doc.clientHeight - 25) + "px" : 'none';
+ style.maxHeight = Conf['Fit Width'] ? frame.clientWidth + "px" : 'none';
+ margin = (current.clientWidth - current.clientHeight) / 2;
+ return style.margin = margin + "px " + (-margin) + "px";
+ } else {
+ return style.maxWidth = style.maxHeight = style.margin = '';
}
}),
setDelay: function() {
@@ -27043,7 +27205,9 @@ Main = (function() {
});
new Notice('warning', msg);
}
- if (!Index.enabled) {
+ if (g.VIEW === 'catalog') {
+ return Main.initCatalog();
+ } else if (!Index.enabled) {
return Main.initThread();
} else {
Main.expectInitFinished = true;
@@ -27205,6 +27369,68 @@ Main = (function() {
}
});
},
+ initCatalog: function() {
+ var board, errors, s, threads;
+ s = g.SITE.selectors.catalog;
+ if (s && (board = $(s.board))) {
+ threads = [];
+ errors = [];
+ Main.addCatalogThreadsObserver = new MutationObserver(Main.addCatalogThreads);
+ Main.addCatalogThreadsObserver.observe(board, {
+ childList: true
+ });
+ Main.parseCatalogThreads($$(s.thread, board), threads, errors);
+ if (errors.length) {
+ Main.handleErrors(errors);
+ }
+ Main.callbackNodes('CatalogThreadNative', threads);
+ }
+ Main.expectInitFinished = true;
+ return $.event('4chanXInitFinished');
+ },
+ parseCatalogThreads: function(threadRoots, threads, errors) {
+ var err, j, len, ref, thread, threadRoot;
+ for (j = 0, len = threadRoots.length; j < len; j++) {
+ threadRoot = threadRoots[j];
+ try {
+ thread = new CatalogThreadNative(threadRoot);
+ if (((ref = thread.thread.catalogViewNative) != null ? ref.nodes.root : void 0) !== threadRoot) {
+ thread.thread.catalogViewNative = thread;
+ threads.push(thread);
+ }
+ } catch (error1) {
+ err = error1;
+ errors.push({
+ message: "Parsing of Catalog Thread No." + ((threadRoot.dataset.id || threadRoot.id).match(/\d+/)) + " failed. Thread will be skipped.",
+ error: err
+ });
+ }
+ }
+ },
+ addCatalogThreads: function(records) {
+ var errors, j, k, len, len1, node, record, ref, threadRoots, threads;
+ threadRoots = [];
+ for (j = 0, len = records.length; j < len; j++) {
+ record = records[j];
+ ref = record.addedNodes;
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ node = ref[k];
+ if (node.nodeType === Node.ELEMENT_NODE && node.matches(g.SITE.selectors.catalog.thread)) {
+ threadRoots.push(node);
+ }
+ }
+ }
+ if (!threadRoots.length) {
+ return;
+ }
+ threads = [];
+ errors = [];
+ Main.parseCatalogThreads(threadRoots, threads, errors);
+ if (errors.length) {
+ Main.handleErrors(errors);
+ }
+ return Main.callbackNodes('CatalogThreadNative', threads);
+ },
callbackNodes: function(klass, nodes) {
var cb, i, node;
i = 0;
diff --git a/builds/4chan-X.crx b/builds/4chan-X.crx
index d89019f00..41dbca539 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 923e0039e..307b34edf 100644
--- a/builds/4chan-X.meta.js
+++ b/builds/4chan-X.meta.js
@@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X
-// @version 1.14.10.0
+// @version 1.14.10.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js
index 7d8f248a2..4ad39eba8 100644
--- a/builds/4chan-X.user.js
+++ b/builds/4chan-X.user.js
@@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X
-// @version 1.14.10.0
+// @version 1.14.10.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@@ -184,7 +184,7 @@
'use strict';
-var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, ModContact, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostJumper, PostRedirect, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, SW, Sauce, Settings, ShimSet, SimpleDict, Site, Test, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, Tinyboard, UI, Unread, UnreadIndex, Volume;
+var $, $$, Anonymize, AntiAutoplay, ArchiveLink, Banner, Board, BoardConfig, CSS, Callbacks, Captcha, CatalogLinks, CatalogThread, CatalogThreadNative, Config, Connection, CopyTextLink, CrossOrigin, CustomCSS, DataBoard, DeleteLink, DownloadLink, Embedding, ExpandComment, ExpandThread, FappeTyme, Favicon, Fetcher, FileInfo, Filter, Flash, Fourchan, Gallery, Get, Header, IDColor, IDHighlight, IDPostCount, ImageCommon, ImageExpand, ImageHost, ImageHover, ImageLoader, Index, Keybinds, Linkify, Main, MarkNewIPs, Menu, Metadata, ModContact, Nav, NormalizeURL, Notice, PSAHiding, PassLink, Polyfill, Post, PostHiding, PostJumper, PostRedirect, PostSuccessful, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, ReplyPruning, Report, ReportLink, RevealSpoilers, SW, Sauce, Settings, ShimSet, SimpleDict, Site, Test, Thread, ThreadHiding, ThreadLinks, ThreadStats, ThreadUpdater, ThreadWatcher, Time, Tinyboard, UI, Unread, UnreadIndex, Volume;
var Conf, E, c, d, doc, docSet, g;
@@ -199,7 +199,7 @@ docSet = function() {
};
g = {
- VERSION: '1.14.10.0',
+ VERSION: '1.14.10.1',
NAMESPACE: '4chan X.',
sites: {},
boards: {}
@@ -286,6 +286,7 @@ Config = (function() {
'Anonymize': [false, 'Make everyone Anonymous.'],
'Filter': [true, 'Self-moderation placebo.'],
'Filtered Backlinks': [false, 'When enabled, shows backlinks to filtered posts with a line-through decoration. Otherwise, hides the backlinks.', 1],
+ 'Filter in Native Catalog': [true, 'Apply 4chan X filters in native catalog.', 1],
'Recursive Hiding': [true, 'Hide replies of hidden posts, recursively.'],
'Thread Hiding Buttons': [true, 'Add buttons to hide entire threads.'],
'Reply Hiding Buttons': [true, 'Add buttons to hide single replies.'],
@@ -512,6 +513,8 @@ Config = (function() {
'Advance Gallery': ['Enter', 'Go to next image or, if Autoplay is off, play video.'],
'Pause': ['p', 'Pause/play videos in the gallery.'],
'Slideshow': ['Ctrl+Right', 'Toggle the gallery slideshow mode.'],
+ 'Rotate image clockwise': ['Shift+Right', 'Rotate image clockwise in gallery.'],
+ 'Rotate image anticlockwise': ['Shift+Left', 'Rotate image anticlockwise in gallery.'],
'fappeTyme': ['f', 'Toggle Fappe Tyme.'],
'werkTyme': ['Shift+w', 'Toggle Werk Tyme.'],
'Front page': ['1', 'Jump to front page.'],
@@ -1482,6 +1485,9 @@ body.hasDropDownNav{\n\
#menu a {\n\
margin: 0;\n\
}\n\
+.gal-buttons.gal-buttons a {\n\
+ font-size: inherit;\n\
+}\n\
/* Anti-autoplay */\n\
audio.controls-added {\n\
display: block;\n\
@@ -1675,7 +1681,8 @@ audio.controls-added {\n\
#toggleMsgBtn {\n\
display: none !important;\n\
}\n\
-#board-list .current {\n\
+.current,\n\
+:root.sw-yotsuba div#boardNavDesktopFoot a.current {\n\
font-weight: bold;\n\
}\n\
@media (min-width: 1300px) {\n\
@@ -2759,7 +2766,8 @@ input[name=\"Default Volume\"] {\n\
:root:not(.werkTyme) .catalog-thread.filter-highlight .catalog-thumb,\n\
:root.werkTyme .catalog-thread.filter-highlight:not(:hover),\n\
:root.werkTyme:not(.catalog-hover-expand) .catalog-thread.filter-highlight,\n\
-:root.werkTyme.catalog-hover-expand .catalog-thread.filter-highlight > .catalog-container:hover > .catalog-post {\n\
+:root.werkTyme.catalog-hover-expand .catalog-thread.filter-highlight > .catalog-container:hover > .catalog-post,\n\
+:root.catalog $site$catalog$thread.filter-highlight$site$relative$catalogHighlight {\n\
box-shadow: 0 0 3px 3px rgba(255, 0, 0, .5);\n\
}\n\
:root:not(.werkTyme) .catalog-thread.watched .catalog-thumb,\n\
@@ -3559,6 +3567,8 @@ a:only-of-type > .remove {\n\
overflow-x: scroll !important;\n\
}\n\
.gal-image a {\n\
+ display: -webkit-flex;\n\
+ display: flex;\n\
margin: auto;\n\
line-height: 0;\n\
max-width: 100%;\n\
@@ -5808,6 +5818,8 @@ Callbacks = (function() {
Callbacks.CatalogThread = new Callbacks('Catalog Thread');
+ Callbacks.CatalogThreadNative = new Callbacks('Catalog Thread');
+
function Callbacks(type) {
this.type = type;
this.keys = [];
@@ -5896,6 +5908,34 @@ CatalogThread = (function() {
}).call(this);
+CatalogThreadNative = (function() {
+ var CatalogThreadNative;
+
+ CatalogThreadNative = (function() {
+ CatalogThreadNative.prototype.toString = function() {
+ return this.ID;
+ };
+
+ function CatalogThreadNative(root) {
+ this.nodes = {
+ root: root,
+ thumb: $(g.SITE.selectors.catalog.thumb, root)
+ };
+ this.siteID = g.SITE.ID;
+ this.boardID = this.nodes.thumb.parentNode.pathname.split(/\/+/)[1];
+ this.board = g.boards[this.boardID] || new Board(this.boardID);
+ this.ID = this.threadID = +(root.dataset.id || root.id).match(/\d*$/)[0];
+ this.thread = this.board.threads[this.ID] || new Thread(this.ID, this.board);
+ }
+
+ return CatalogThreadNative;
+
+ })();
+
+ return CatalogThreadNative;
+
+}).call(this);
+
Connection = (function() {
var Connection,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
@@ -7695,11 +7735,17 @@ SW = {};
relative: {
opHighlight: ' > .op',
replyPost: '.reply',
- replyOriginal: 'div[id^="reply_"]:not(.hidden)'
+ replyOriginal: 'div[id^="reply_"]:not(.hidden)',
+ catalogHighlight: ' > .thread'
},
comment: '.body',
spoiler: '.spoiler',
quotelink: 'a[onclick^="highlightReply("]',
+ catalog: {
+ board: '#Grid',
+ thread: '.mix',
+ thumb: '.thread-image'
+ },
boardList: '.boardlist',
boardListBottom: '.boardlist.bottom',
styleSheet: '#stylesheet',
@@ -7803,6 +7849,9 @@ SW = {};
},
isLinkified: function(link) {
return /\bnofollow\b/.test(link.rel);
+ },
+ catalogPin: function(threadRoot) {
+ return threadRoot.dataset.sticky = 'true';
}
};
@@ -7909,11 +7958,17 @@ SW = {};
relative: {
opHighlight: '.opContainer',
replyPost: ' > .reply',
- replyOriginal: '.replyContainer:not([data-clone])'
+ replyOriginal: '.replyContainer:not([data-clone])',
+ catalogHighlight: ''
},
comment: '.postMessage',
spoiler: 's',
quotelink: ':not(pre) > .quotelink',
+ catalog: {
+ board: '#threads',
+ thread: '.thread',
+ thumb: '.thumb'
+ },
boardList: '#boardNavDesktop > .boardList',
boardListBottom: '#boardNavDesktopFoot > .boardList',
styleSheet: 'link[title=switch]',
@@ -8813,7 +8868,10 @@ Filter = (function() {
results: {},
init: function() {
var base, base1, boards, err, excludes, file, filter, hide, hl, i, isstring, j, key, len, len1, line, mask, noti, op, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, regexp, stub, top, type, types;
- if (!(((ref = g.VIEW) === 'index' || ref === 'thread') && Conf['Filter'])) {
+ if (!(((ref = g.VIEW) === 'index' || ref === 'thread' || ref === 'catalog') && Conf['Filter'])) {
+ return;
+ }
+ if (g.VIEW === 'catalog' && !Conf['Filter in Native Catalog']) {
return;
}
if (!Conf['Filtered Backlinks']) {
@@ -8903,10 +8961,14 @@ Filter = (function() {
if (!Object.keys(this.filters).length) {
return;
}
- return Callbacks.Post.push({
- name: 'Filter',
- cb: this.node
- });
+ if (g.VIEW === 'catalog') {
+ return Filter.catalog();
+ } else {
+ return Callbacks.Post.push({
+ name: 'Filter',
+ cb: this.node
+ });
+ }
},
parseBoards: function(boardsRaw) {
var boardID, boardID2, boards, i, j, len, len1, ref, ref1, ref2, ref3, site, siteFilter, siteID;
@@ -9026,6 +9088,68 @@ Filter = (function() {
return Unread.openNotification(this, ' triggered a notification filter');
}
},
+ catalog: function() {
+ var base, url;
+ if (!(url = typeof (base = g.SITE.urls).catalogJSON === "function" ? base.catalogJSON(g.BOARD) : void 0)) {
+ return;
+ }
+ Filter.catalogData = {};
+ $.ajax(url, {
+ onloadend: Filter.catalogParse
+ });
+ return Callbacks.CatalogThreadNative.push({
+ name: 'Filter',
+ cb: this.catalogNode
+ });
+ },
+ catalogParse: function() {
+ var i, item, j, len, len1, page, ref, ref1, ref2;
+ if ((ref = this.status) !== 200 && ref !== 404) {
+ new Notice('warning', "Failed to fetch catalog JSON data. " + (this.status ? "Error " + this.statusText + " (" + this.status + ")" : 'Connection Error'), 1);
+ return;
+ }
+ ref1 = this.response;
+ for (i = 0, len = ref1.length; i < len; i++) {
+ page = ref1[i];
+ ref2 = page.threads;
+ for (j = 0, len1 = ref2.length; j < len1; j++) {
+ item = ref2[j];
+ Filter.catalogData[item.no] = item;
+ }
+ }
+ g.BOARD.threads.forEach(function(thread) {
+ if (thread.catalogViewNative) {
+ return Filter.catalogNode.call(thread.catalogViewNative);
+ }
+ });
+ },
+ catalogNode: function() {
+ var base, hide, hl, ref, ref1, top;
+ if (!(this.boardID === g.BOARD.ID && Filter.catalogData[this.ID])) {
+ return;
+ }
+ if ((ref = QuoteYou.db) != null ? ref.get({
+ siteID: g.SITE.ID,
+ boardID: this.boardID,
+ threadID: this.ID,
+ postID: this.ID
+ }) : void 0) {
+ return;
+ }
+ ref1 = Filter.test(g.SITE.Build.parseJSON(Filter.catalogData[this.ID], this)), hide = ref1.hide, hl = ref1.hl, top = ref1.top;
+ if (hide) {
+ return this.nodes.root.hidden = true;
+ } else {
+ if (hl) {
+ this.highlights = hl;
+ $.addClass.apply($, [this.nodes.root].concat(slice.call(hl)));
+ }
+ if (top) {
+ $.prepend(this.nodes.root.parentNode, this.nodes.root);
+ return typeof (base = g.SITE).catalogPin === "function" ? base.catalogPin(this.nodes.root) : void 0;
+ }
+ }
+ },
isHidden: function(post) {
return !!Filter.test(post).hide;
},
@@ -12915,6 +13039,11 @@ Settings = (function() {
set('fourchanImageHost', (data['Use Faster Image Host'] ? 'i.4cdn.org' : ''));
}
}
+ if (compareString < '00001.00014.00010.00001') {
+ if (data['Filter in Native Catalog'] == null) {
+ set('Filter in Native Catalog', false);
+ }
+ }
return changes;
},
loadSettings: function(data, cb) {
@@ -12975,7 +13104,7 @@ Settings = (function() {
};
});
$.extend(div, {
- innerHTML: "Filter is disabled.
Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 and Unique ID filtering use exact string matching, not regular expressions.
You can use these settings with each regular expression, separate them with semicolons:- Per boards, separate them with commas. It is global if not specified. Use
sfw and nsfw to reference all worksafe or not-worksafe boards.
For example: boards:a,jp;.
To specify boards on a particular site, put the beginning of the domain and a slash character before the list.
Any initial www. should not be included, and all 4chan domains are considered 4chan.org.
For example: boards:4:a,jp,sama:a,z;.
An asterisk can be used to specify all boards on a site.
For example: boards:4:*;.
- Select boards to be excluded from the filter. The syntax is the same as for the
boards: option above.
For example: exclude:vg,v;. - Filter OPs only along with their threads (`only`) or replies only (`no`).
For example: op:only; or op:no;. - Filter only posts with files (`only`) or only posts without files (`no`).
For example: file:only; or file:no;. - Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
For example: stub:yes; or stub:no;. - Highlight instead of hiding. You can specify a class name to use with a userstyle.
For example: highlight; or highlight:wallpaper;. - Highlighted OPs will have their threads put on top of the board index by default.
For example: top:yes; or top:no;. - Show a desktop notification instead of hiding.
For example: notify;. - Filters in the \"General\" section apply to multiple fields, by default
subject,name,filename,comment.
The fields can be specified with the type option, separated by commas.
For example: type:" + E.cat(filterTypes) + ";.
Types can also be combined with a + sign; this indicates the filter applies to the given fields joined by newlines.
For example: type:filename+filesize+dimensions;.
Note: If you're using the native catalog rather than 4chan X's catalog, 4chan X's filters do not apply there.
The native catalog has its own separate filter list.
"
+ innerHTML: "Filter is disabled.
Use regular expressions, one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 and Unique ID filtering use exact string matching, not regular expressions.
You can use these settings with each regular expression, separate them with semicolons:- Per boards, separate them with commas. It is global if not specified. Use
sfw and nsfw to reference all worksafe or not-worksafe boards.
For example: boards:a,jp;.
To specify boards on a particular site, put the beginning of the domain and a slash character before the list.
Any initial www. should not be included, and all 4chan domains are considered 4chan.org.
For example: boards:4:a,jp,sama:a,z;.
An asterisk can be used to specify all boards on a site.
For example: boards:4:*;.
- Select boards to be excluded from the filter. The syntax is the same as for the
boards: option above.
For example: exclude:vg,v;. - Filter OPs only along with their threads (`only`) or replies only (`no`).
For example: op:only; or op:no;. - Filter only posts with files (`only`) or only posts without files (`no`).
For example: file:only; or file:no;. - Overrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
For example: stub:yes; or stub:no;. - Highlight instead of hiding. You can specify a class name to use with a userstyle.
For example: highlight; or highlight:wallpaper;. - Highlighted OPs will have their threads put on top of the board index by default.
For example: top:yes; or top:no;. - Show a desktop notification instead of hiding.
For example: notify;. - Filters in the \"General\" section apply to multiple fields, by default
subject,name,filename,comment.
The fields can be specified with the type option, separated by commas.
For example: type:" + E.cat(filterTypes) + ";.
Types can also be combined with a + sign; this indicates the filter applies to the given fields joined by newlines.
For example: type:filename+filesize+dimensions;.
"
});
return $('.warning', div).hidden = Conf['Filter'];
},
@@ -14253,6 +14382,10 @@ Gallery = (function() {
return Gallery.cb.pause;
case Conf['Slideshow']:
return Gallery.cb.toggleSlideshow;
+ case Conf['Rotate image anticlockwise']:
+ return Gallery.cb.rotateLeft;
+ case Conf['Rotate image clockwise']:
+ return Gallery.cb.rotateRight;
}
})();
if (!cb) {
@@ -14332,6 +14465,22 @@ Gallery = (function() {
$.rmClass(Gallery.nodes.buttons, 'gal-playing');
return Gallery.slideshow = false;
},
+ rotateLeft: function() {
+ return Gallery.cb.rotate(270);
+ },
+ rotateRight: function() {
+ return Gallery.cb.rotate(90);
+ },
+ rotate: $.debounce(100, function(delta) {
+ var current;
+ current = Gallery.nodes.current;
+ if (current.nodeName === 'IFRAME') {
+ return;
+ }
+ current.dataRotate = ((current.dataRotate || 0) + delta) % 360;
+ current.style.transform = "rotate(" + current.dataRotate + "deg)";
+ return Gallery.cb.setHeight();
+ }),
close: function() {
$.off(Gallery.nodes.current, 'error', Gallery.error);
ImageCommon.pause(Gallery.nodes.current);
@@ -14360,16 +14509,29 @@ Gallery = (function() {
return (this.checked ? $.addClass : $.rmClass)(doc, "gal-" + (this.name.toLowerCase().replace(/\s+/g, '-')));
},
setHeight: $.debounce(100, function() {
- var current, dim, frame, height, minHeight, ref, ref1, ref2, style, width;
+ var containerHeight, containerWidth, current, dim, frame, height, margin, minHeight, ref, ref1, ref2, ref3, style, width;
ref = Gallery.nodes, current = ref.current, frame = ref.frame;
style = current.style;
if (Conf['Stretch to Fit'] && (dim = (ref1 = g.posts[current.dataset.post]) != null ? ref1.file.dimensions : void 0)) {
ref2 = dim.split('x'), width = ref2[0], height = ref2[1];
- minHeight = Math.min(doc.clientHeight - 25, height / width * frame.clientWidth);
+ containerWidth = frame.clientWidth;
+ containerHeight = doc.clientHeight - 25;
+ if ((current.dataRotate || 0) % 180 === 90) {
+ ref3 = [containerHeight, containerWidth], containerWidth = ref3[0], containerHeight = ref3[1];
+ }
+ minHeight = Math.min(containerHeight, height / width * containerWidth);
style.minHeight = minHeight + 'px';
- return style.minWidth = (width / height * minHeight) + 'px';
+ style.minWidth = (width / height * minHeight) + 'px';
} else {
- return style.minHeight = style.minWidth = '';
+ style.minHeight = style.minWidth = '';
+ }
+ if ((current.dataRotate || 0) % 180 === 90) {
+ style.maxWidth = Conf['Fit Height'] ? (doc.clientHeight - 25) + "px" : 'none';
+ style.maxHeight = Conf['Fit Width'] ? frame.clientWidth + "px" : 'none';
+ margin = (current.clientWidth - current.clientHeight) / 2;
+ return style.margin = margin + "px " + (-margin) + "px";
+ } else {
+ return style.maxWidth = style.maxHeight = style.margin = '';
}
}),
setDelay: function() {
@@ -27043,7 +27205,9 @@ Main = (function() {
});
new Notice('warning', msg);
}
- if (!Index.enabled) {
+ if (g.VIEW === 'catalog') {
+ return Main.initCatalog();
+ } else if (!Index.enabled) {
return Main.initThread();
} else {
Main.expectInitFinished = true;
@@ -27205,6 +27369,68 @@ Main = (function() {
}
});
},
+ initCatalog: function() {
+ var board, errors, s, threads;
+ s = g.SITE.selectors.catalog;
+ if (s && (board = $(s.board))) {
+ threads = [];
+ errors = [];
+ Main.addCatalogThreadsObserver = new MutationObserver(Main.addCatalogThreads);
+ Main.addCatalogThreadsObserver.observe(board, {
+ childList: true
+ });
+ Main.parseCatalogThreads($$(s.thread, board), threads, errors);
+ if (errors.length) {
+ Main.handleErrors(errors);
+ }
+ Main.callbackNodes('CatalogThreadNative', threads);
+ }
+ Main.expectInitFinished = true;
+ return $.event('4chanXInitFinished');
+ },
+ parseCatalogThreads: function(threadRoots, threads, errors) {
+ var err, j, len, ref, thread, threadRoot;
+ for (j = 0, len = threadRoots.length; j < len; j++) {
+ threadRoot = threadRoots[j];
+ try {
+ thread = new CatalogThreadNative(threadRoot);
+ if (((ref = thread.thread.catalogViewNative) != null ? ref.nodes.root : void 0) !== threadRoot) {
+ thread.thread.catalogViewNative = thread;
+ threads.push(thread);
+ }
+ } catch (error1) {
+ err = error1;
+ errors.push({
+ message: "Parsing of Catalog Thread No." + ((threadRoot.dataset.id || threadRoot.id).match(/\d+/)) + " failed. Thread will be skipped.",
+ error: err
+ });
+ }
+ }
+ },
+ addCatalogThreads: function(records) {
+ var errors, j, k, len, len1, node, record, ref, threadRoots, threads;
+ threadRoots = [];
+ for (j = 0, len = records.length; j < len; j++) {
+ record = records[j];
+ ref = record.addedNodes;
+ for (k = 0, len1 = ref.length; k < len1; k++) {
+ node = ref[k];
+ if (node.nodeType === Node.ELEMENT_NODE && node.matches(g.SITE.selectors.catalog.thread)) {
+ threadRoots.push(node);
+ }
+ }
+ }
+ if (!threadRoots.length) {
+ return;
+ }
+ threads = [];
+ errors = [];
+ Main.parseCatalogThreads(threadRoots, threads, errors);
+ if (errors.length) {
+ Main.handleErrors(errors);
+ }
+ return Main.callbackNodes('CatalogThreadNative', threads);
+ },
callbackNodes: function(klass, nodes) {
var cb, i, node;
i = 0;
diff --git a/builds/4chan-X.zip b/builds/4chan-X.zip
index 2a2517fd0..3e491d86e 100644
Binary files a/builds/4chan-X.zip and b/builds/4chan-X.zip differ
diff --git a/builds/updates-beta.json b/builds/updates-beta.json
index 087f887e8..0eebd052a 100644
--- a/builds/updates-beta.json
+++ b/builds/updates-beta.json
@@ -3,7 +3,7 @@
"4chan-x@4chan-x.net": {
"updates": [
{
- "version": "1.14.10.0",
+ "version": "1.14.10.1",
"update_link": "https://www.4chan-x.net/builds/4chan-X-beta.crx"
}
]
diff --git a/builds/updates-beta.xml b/builds/updates-beta.xml
index 12430b9de..f1e0c45fd 100644
--- a/builds/updates-beta.xml
+++ b/builds/updates-beta.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/builds/updates.json b/builds/updates.json
index 9704693d3..67401d5de 100644
--- a/builds/updates.json
+++ b/builds/updates.json
@@ -3,7 +3,7 @@
"4chan-x@4chan-x.net": {
"updates": [
{
- "version": "1.14.10.0",
+ "version": "1.14.10.1",
"update_link": "https://www.4chan-x.net/builds/4chan-X.crx"
}
]
diff --git a/builds/updates.xml b/builds/updates.xml
index e5cce0aa8..7a53d5b33 100644
--- a/builds/updates.xml
+++ b/builds/updates.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/version.json b/version.json
index f97181395..4bfbdba1e 100644
--- a/version.json
+++ b/version.json
@@ -1,4 +1,4 @@
{
- "version": "1.14.10.0",
- "date": "2019-07-17T20:56:58.626Z"
+ "version": "1.14.10.1",
+ "date": "2019-07-19T02:39:09.742Z"
}
\ No newline at end of file