diff --git a/CHANGELOG.md b/CHANGELOG.md index ab0fba21b..93e53c322 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ -Sometimes the changelog has notes (not comprehensive) acknowledging people's work. This does not mean the changes are their fault, only that their code was used. All changes to the script are chosen by and the fault of the maintainer (ccd0). +### v1.14.15 + +**v1.14.15.0** *(2019-12-13)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.15.0/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.15.0/builds/4chan-X-noupdate.crx)] +- Based on v1.14.14.5. +- Support archived threads on kissu.moe. +- Fix display of board names containing special characters in custom board list. #2473 +- Increase link title cache size. #2327 +- Add option to use your own Youtube API key. #2327 +- %URL in Sauce uses full image when extension is .jpeg. + ### v1.14.14 **v1.14.14.5** *(2019-12-13)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.14.5/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.14.5/builds/4chan-X-noupdate.crx)] diff --git a/builds/4chan-X-beta.crx b/builds/4chan-X-beta.crx index 30a9a060a..e9fe7f689 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 568ae7912..c42373979 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.14.5 +// @version 1.14.15.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X diff --git a/builds/4chan-X-beta.user.js b/builds/4chan-X-beta.user.js index e13088227..7b7860dba 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.14.5 +// @version 1.14.15.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -211,7 +211,7 @@ docSet = function() { }; g = { - VERSION: '1.14.14.5', + VERSION: '1.14.15.0', NAMESPACE: '4chan X.', sites: Object.create(null), boards: Object.create(null) @@ -591,7 +591,8 @@ Config = (function() { knownBanners: '0.jpg,1.jpg,2.jpg,4.jpg,6.jpg,7.jpg,8.jpg,9.jpg,10.jpg,11.jpg,12.jpg,13.jpg,14.jpg,16.jpg,17.jpg,18.jpg,19.jpg,20.jpg,21.jpg,22.jpg,24.jpg,25.jpg,26.jpg,28.jpg,29.jpg,33.jpg,38.jpg,39.jpg,43.jpg,44.jpg,45.jpg,46.jpg,47.jpg,52.jpg,54.jpg,57.jpg,59.jpg,60.jpg,61.jpg,64.jpg,66.jpg,67.jpg,69.jpg,71.jpg,72.jpg,76.jpg,77.jpg,81.jpg,82.jpg,83.jpg,84.jpg,88.jpg,90.jpg,91.jpg,96.jpg,98.jpg,99.jpg,100.jpg,104.jpg,106.jpg,116.jpg,119.jpg,137.jpg,140.jpg,148.jpg,149.jpg,150.jpg,154.jpg,156.jpg,157.jpg,158.jpg,159.jpg,161.jpg,162.jpg,164.jpg,165.jpg,166.jpg,167.jpg,168.jpg,169.jpg,170.jpg,171.jpg,172.jpg,173.jpg,174.jpg,175.jpg,176.jpg,178.jpg,179.jpg,180.jpg,181.jpg,182.jpg,183.jpg,186.jpg,189.jpg,190.jpg,192.jpg,193.jpg,194.jpg,197.jpg,198.jpg,200.jpg,201.jpg,202.jpg,203.jpg,205.jpg,206.jpg,207.jpg,208.jpg,210.jpg,213.jpg,214.jpg,215.jpg,216.jpg,218.jpg,219.jpg,220.jpg,221.jpg,222.jpg,223.jpg,224.jpg,227.jpg,0.png,1.png,2.png,3.png,5.png,6.png,9.png,10.png,11.png,12.png,14.png,16.png,19.png,20.png,21.png,22.png,23.png,24.png,26.png,27.png,28.png,29.png,30.png,31.png,32.png,33.png,34.png,37.png,39.png,40.png,41.png,42.png,43.png,44.png,45.png,48.png,49.png,50.png,51.png,52.png,53.png,57.png,58.png,59.png,64.png,66.png,67.png,68.png,69.png,70.png,71.png,72.png,76.png,78.png,79.png,81.png,82.png,85.png,86.png,87.png,89.png,95.png,98.png,100.png,101.png,102.png,105.png,106.png,107.png,109.png,110.png,111.png,112.png,113.png,114.png,115.png,116.png,118.png,119.png,120.png,121.png,122.png,123.png,126.png,128.png,130.png,134.png,136.png,138.png,139.png,140.png,142.png,145.png,146.png,149.png,150.png,151.png,152.png,153.png,154.png,155.png,156.png,157.png,158.png,159.png,160.png,163.png,164.png,165.png,166.png,167.png,168.png,169.png,170.png,171.png,172.png,173.png,174.png,178.png,179.png,180.png,181.png,182.png,184.png,186.png,188.png,190.png,192.png,193.png,194.png,195.png,196.png,197.png,198.png,200.png,202.png,203.png,205.png,206.png,207.png,209.png,212.png,213.png,214.png,216.png,217.png,218.png,219.png,220.png,221.png,222.png,223.png,224.png,225.png,226.png,229.png,231.png,232.png,233.png,234.png,235.png,237.png,238.png,239.png,240.png,241.png,242.png,244.png,245.png,246.png,247.png,248.png,249.png,250.png,253.png,254.png,255.png,256.png,257.png,258.png,259.png,260.png,262.png,268.png,0.gif,1.gif,2.gif,3.gif,4.gif,5.gif,6.gif,7.gif,8.gif,9.gif,10.gif,12.gif,13.gif,14.gif,15.gif,16.gif,18.gif,19.gif,20.gif,21.gif,22.gif,23.gif,24.gif,28.gif,29.gif,30.gif,33.gif,34.gif,35.gif,36.gif,37.gif,39.gif,40.gif,42.gif,44.gif,45.gif,46.gif,48.gif,50.gif,52.gif,54.gif,55.gif,57.gif,58.gif,59.gif,60.gif,61.gif,63.gif,64.gif,66.gif,67.gif,68.gif,69.gif,70.gif,72.gif,73.gif,75.gif,76.gif,77.gif,78.gif,80.gif,81.gif,82.gif,83.gif,86.gif,87.gif,88.gif,92.gif,93.gif,94.gif,95.gif,96.gif,97.gif,98.gif,99.gif,100.gif,101.gif,102.gif,103.gif,104.gif,105.gif,106.gif,108.gif,109.gif,110.gif,111.gif,112.gif,113.gif,115.gif,116.gif,117.gif,118.gif,119.gif,120.gif,122.gif,123.gif,124.gif,127.gif,129.gif,130.gif,131.gif,134.gif,135.gif,136.gif,138.gif,139.gif,141.gif,144.gif,146.gif,148.gif,149.gif,153.gif,154.gif,155.gif,157.gif,158.gif,159.gif,160.gif,161.gif,162.gif,164.gif,166.gif,167.gif,168.gif,169.gif,170.gif,171.gif,172.gif,173.gif,174.gif,175.gif,176.gif,177.gif,178.gif,181.gif,182.gif,183.gif,185.gif,186.gif,187.gif,188.gif,189.gif,190.gif,191.gif,192.gif,193.gif,195.gif,196.gif,197.gif,200.gif,201.gif,202.gif,203.gif,204.gif,205.gif,206.gif,207.gif,208.gif,209.gif,210.gif,211.gif,212.gif,213.gif,214.gif,215.gif,216.gif,217.gif,219.gif,220.gif,221.gif,222.gif,224.gif,225.gif,226.gif,227.gif,228.gif,230.gif,232.gif,233.gif,234.gif,235.gif,238.gif,240.gif,241.gif,243.gif,244.gif,245.gif,246.gif,247.gif,249.gif,250.gif,251.gif,253.gif', cachedTitles: [[]], passMessageClosed: false, - 'Prerequest Captcha': false + 'Prerequest Captcha': false, + youtubeAPIKey: 'AIzaSyB5_zaen_-46Uhz1xGR-lz1YoUMHqCD6CE' }; return Config; @@ -6405,7 +6406,7 @@ DataBoard = (function() { } response1 = this.response; return $.cache(archiveList, function() { - if (this.status !== 200) { + if (!(this.status === 200 || (!g.SITE.archivedBoardsKnown && this.status === 404))) { return; } return that.ajaxCleanParse(boardID, response1, this.response); @@ -6887,7 +6888,9 @@ Post = (function() { ref = ['isSticky', 'isClosed', 'isArchived']; for (j = 0, len = ref.length; j < len; j++) { key = ref[j]; - this.thread[key] = (selector = g.SITE.selectors.icons[key]) ? !!$(selector, this.nodes.info) : false; + if ((selector = g.SITE.selectors.icons[key])) { + this.thread[key] = !!$(selector, this.nodes.info); + } } if (this.thread.isArchived) { this.thread.isClosed = true; @@ -7749,10 +7752,10 @@ SW = {}; return false; }, urls: { - thread: function(arg) { + thread: function(arg, isArchived) { var boardID, ref, siteID, threadID; siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID; - return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/res/" + threadID + ".html"; + return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/" + (isArchived ? 'archive/' : '') + "res/" + threadID + ".html"; }, post: function(arg) { var postID; @@ -7769,16 +7772,19 @@ SW = {}; siteID = arg.siteID, boardID = arg.boardID; return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/catalog.html"; }, - threadJSON: function(arg) { + threadJSON: function(arg, isArchived) { var boardID, ref, root, siteID, threadID; siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID; root = (ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0; if (root) { - return "" + root + boardID + "/res/" + threadID + ".json"; + return "" + root + boardID + "/" + (isArchived ? 'archive/' : '') + "res/" + threadID + ".json"; } else { return ''; } }, + archivedThreadJSON: function(thread) { + return SW.tinyboard.urls.threadJSON(thread, true); + }, threadsListJSON: function(arg) { var boardID, ref, root, siteID; siteID = arg.siteID, boardID = arg.boardID; @@ -7789,6 +7795,16 @@ SW = {}; return ''; } }, + archiveListJSON: function(arg) { + var boardID, ref, root, siteID; + siteID = arg.siteID, boardID = arg.boardID; + root = (ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0; + if (root) { + return "" + root + boardID + "/archive/archive.json"; + } else { + return ''; + } + }, catalogJSON: function(arg) { var boardID, ref, root, siteID; siteID = arg.siteID, boardID = arg.boardID; @@ -7991,6 +8007,7 @@ SW = {}; SW.yotsuba = { isOPContainerThread: false, hasIPCount: true, + archivedBoardsKnown: true, urls: { thread: function(arg) { var boardID, threadID; @@ -10817,7 +10834,7 @@ Header = (function() { } else { a = $.el('a', { href: "/" + g.BOARD.ID + "/", - textContent: text || g.BOARD.ID, + textContent: text || decodeURIComponent(g.BOARD.ID), className: 'current' }); if (/-nt/.test(t)) { @@ -13444,7 +13461,7 @@ Settings = (function() { }, advanced: function(section) { var applyCSS, boardSelect, customCSS, event, input, inputs, interval, items, itemsArchive, j, k, l, len, len1, len2, len3, listImageHost, m, name, ref, ref1, ref2, ref3, ref4, table, textContent, updateArchives, warning; - $.extend(section, {innerHTML: "
Archives
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection

Archive Lists: Each line below should be an archive list in this format or a URL to load an archive list from.
Archive properties can be overriden by another item with the same uid (or if absent, its name).
Last updated:
External Catalog
External Catalog is disabled. This will be used only as a fallback.
URLs of external catalog sites, where %board is to be replaced by the board name.
Each URL should be followed by ;boards: and optionally ;exclude: and a list of supported/excluded boards in the format explained in the Filter guide.
Override 4chan Image Host
Change 4chan image links to this domain. Leave blank for no change.
Captcha Language
Choose from list of language codes. Leave blank to autoselect.
Captcha Solving Service
Supported services include captcha.guru, 2captcha, and any other service implementing the 2captcha API.
Leave blank to disable.
Domain: API Key:
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
Index mode: g-mode:"infinite scrolling"
Index sort: g-sort:"creation date rev"
External link: external-text:"Google","http://www.google.com"
Open in new tab: g-nt
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:"Piracy"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Supported format specifiers:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Language tag:
Quote Backlinks formatting is disabled.
:
Default pasted content filename
.png
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (4chan filename)
Filename: %n (truncated), %N (untruncated), %t (4chan filename)
Download button: %d
Quick filter MD5: %f
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
Tag: %g
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input's auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
For more information about customizing 4chan X's CSS, see the styling guide.
Javascript Whitelist
Sources from which Javascript is allowed to be loaded by Content Security Policy.
Lines starting with a # will be ignored.
Known Banners
List of known banners, used for click-to-change feature.
"}); + $.extend(section, {innerHTML: "
Archives
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection

Archive Lists: Each line below should be an archive list in this format or a URL to load an archive list from.
Archive properties can be overriden by another item with the same uid (or if absent, its name).
Last updated:
External Catalog
External Catalog is disabled. This will be used only as a fallback.
URLs of external catalog sites, where %board is to be replaced by the board name.
Each URL should be followed by ;boards: and optionally ;exclude: and a list of supported/excluded boards in the format explained in the Filter guide.
Override 4chan Image Host
Change 4chan image links to this domain. Leave blank for no change.
Captcha Language
Choose from list of language codes. Leave blank to autoselect.
Captcha Solving Service
Supported services include captcha.guru, 2captcha, and any other service implementing the 2captcha API.
Leave blank to disable.
Domain: API Key:
Youtube API Key
API key used to fetch Youtube link titles. You can obtain your own key here to make the feature work when the default key exceeds its quota.
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
Index mode: g-mode:"infinite scrolling"
Index sort: g-sort:"creation date rev"
External link: external-text:"Google","http://www.google.com"
Open in new tab: g-nt
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:"Piracy"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Supported format specifiers:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Language tag:
Quote Backlinks formatting is disabled.
:
Default pasted content filename
.png
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (4chan filename)
Filename: %n (truncated), %N (untruncated), %t (4chan filename)
Download button: %d
Quick filter MD5: %f
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
Tag: %g
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input's auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
For more information about customizing 4chan X's CSS, see the styling guide.
Javascript Whitelist
Sources from which Javascript is allowed to be loaded by Content Security Policy.
Lines starting with a # will be ignored.
Known Banners
List of known banners, used for click-to-change feature.
"}); ref = $$('.warning', section); for (j = 0, len = ref.length; j < len; j++) { warning = ref[j]; @@ -14969,7 +14986,7 @@ ImageCommon = (function() { return g.SITE.software === 'yotsuba' && !ImageHost.test(file.src.split('/')[2]); }, error: function(file, post, fileObj, delay, cb) { - var base, redirect, src, threadJSON, timeoutID, url; + var base, parseJSON, redirect, src, threadJSON, timeoutID, url; src = fileObj.url.split('/'); url = null; if (g.SITE.software === 'yotsuba' && Conf['404 Redirect']) { @@ -15004,32 +15021,43 @@ ImageCommon = (function() { if (!threadJSON) { return; } - return $.ajax(threadJSON, { - onloadend: function() { - var i, len, postObj, ref, ref1; - if (this.status === 404) { + parseJSON = function(isArchiveURL) { + var archivedThreadJSON, base1, i, len, postObj, ref, ref1; + if (this.status === 404) { + if (!isArchiveURL && (archivedThreadJSON = typeof (base1 = g.SITE.urls).archivedThreadJSON === "function" ? base1.archivedThreadJSON(post) : void 0)) { + $.ajax(archivedThreadJSON, { + onloadend: function() { + return parseJSON.call(this, true); + } + }); + } else { post.kill(!post.isClone, fileObj.index); } - if (this.status !== 200) { - return redirect(); - } - ref = this.response.posts; - for (i = 0, len = ref.length; i < len; i++) { - postObj = ref[i]; - if (postObj.no === post.ID) { - break; - } - } - if (postObj.no !== post.ID) { - post.kill(); - return redirect(); - } else if (ref1 = fileObj.docIndex, indexOf.call(g.SITE.Build.parseJSON(postObj, post.board).filesDeleted, ref1) >= 0) { - post.kill(true); - return redirect(); - } else { - return url = fileObj.url; + } + if (this.status !== 200) { + return redirect(); + } + ref = this.response.posts; + for (i = 0, len = ref.length; i < len; i++) { + postObj = ref[i]; + if (postObj.no === post.ID) { + break; } } + if (postObj.no !== post.ID) { + post.kill(); + return redirect(); + } else if (ref1 = fileObj.docIndex, indexOf.call(g.SITE.Build.parseJSON(postObj, post.board).filesDeleted, ref1) >= 0) { + post.kill(true); + return redirect(); + } else { + return url = fileObj.url; + } + }; + return $.ajax(threadJSON, { + onloadend: function() { + return parseJSON.call(this); + } }); }, addControls: function(video) { @@ -16181,7 +16209,7 @@ Sauce = (function() { return file.url; }, IMG: function(post, file, ext) { - if (ext === 'gif' || ext === 'jpg' || ext === 'png') { + if (ext === 'gif' || ext === 'jpg' || ext === 'jpeg' || ext === 'png') { return file.url; } else { return file.thumbURL; @@ -16632,7 +16660,7 @@ Embedding = (function() { cachedTitles = arg.cachedTitles; sync(cachedTitles); try { - cachedTitles = newEntries.concat(cachedTitles).slice(-100); + cachedTitles = newEntries.concat(cachedTitles).slice(-1000); } catch (error) { cachedTitles = newEntries; } @@ -17224,7 +17252,7 @@ Embedding = (function() { api: function(uids) { var ids, key; ids = encodeURIComponent(uids.join(',')); - key = 'AIzaSyB5_zaen_-46Uhz1xGR-lz1YoUMHqCD6CE'; + key = Conf['youtubeAPIKey']; return "https://www.googleapis.com/youtube/v3/videos?part=snippet&id=" + ids + "&fields=items%28id%2Csnippet%28title%29%29&key=" + key; }, text: function(data, uid) { @@ -21703,26 +21731,13 @@ ThreadWatcher = (function() { threadID: threadID }); nKilled++; - } else if (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) { + } else { ThreadWatcher.fetchStatus({ siteID: siteID, boardID: boardID, threadID: threadID, data: data }); - } else { - db.extend({ - boardID: boardID, - threadID: threadID, - val: { - isDead: true, - page: void 0, - lastPage: void 0, - unread: void 0, - quotingYou: void 0 - } - }); - nKilled++; } } if (nKilled) { @@ -21985,13 +22000,7 @@ ThreadWatcher = (function() { } } } else { - if (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) { - ThreadWatcher.fetchStatus(thread); - } else { - ThreadWatcher.update(siteID, boardID, threadID, { - isDead: true - }); - } + ThreadWatcher.fetchStatus(thread); } } }, @@ -22017,19 +22026,19 @@ ThreadWatcher = (function() { force: force }, [thread], ThreadWatcher.parseStatus); }, - parseStatus: function(arg) { - var boardID, data, isDead, j, last, lastReadPost, len1, match, newData, postObj, quotesYou, quotingYou, ref, ref1, ref2, regexp, replies, site, siteID, threadID, unread, youOP; - siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID, data = arg.data, newData = arg.newData; + parseStatus: function(thread, isArchiveURL) { + var archiveURL, base, boardID, data, force, isArchived, isDead, j, last, lastReadPost, len1, match, newData, postObj, quotesYou, quotingYou, ref, ref1, ref2, ref3, regexp, replies, site, siteID, threadID, unread, youOP; + siteID = thread.siteID, boardID = thread.boardID, threadID = thread.threadID, data = thread.data, newData = thread.newData, force = thread.force; site = g.sites[siteID]; if (this.status === 200 && this.response) { last = this.response.posts[this.response.posts.length - 1].no; replies = this.response.posts.length - 1; - isDead = !!this.response.posts[0].archived; + isDead = isArchived = !!(this.response.posts[0].archived || isArchiveURL); if (isDead && Conf['Auto Prune']) { ThreadWatcher.rm(siteID, boardID, threadID); return; } - if (last === data.last && isDead === data.isDead) { + if (last === data.last && isDead === data.isDead && isArchived === data.isArchived) { return; } lastReadPost = ThreadWatcher.unreaddb.get({ @@ -22096,12 +22105,23 @@ ThreadWatcher = (function() { last: last, replies: replies, isDead: isDead, + isArchived: isArchived, unread: unread, quotingYou: quotingYou }); return ThreadWatcher.update(siteID, boardID, threadID, newData); } else if (this.status === 404) { - if (site.mayLackJSON && (data.last == null)) { + archiveURL = (ref3 = g.sites[siteID]) != null ? typeof (base = ref3.urls).archivedThreadJSON === "function" ? base.archivedThreadJSON({ + siteID: siteID, + boardID: boardID, + threadID: threadID + }) : void 0 : void 0; + if (!isArchiveURL && archiveURL) { + return ThreadWatcher.fetch(archiveURL, { + siteID: siteID, + force: force + }, [thread, true], ThreadWatcher.parseStatus); + } else if (site.mayLackJSON && (data.last == null)) { return ThreadWatcher.update(siteID, boardID, threadID, { last: -1 }); @@ -22143,13 +22163,13 @@ ThreadWatcher = (function() { return all; }, makeLine: function(siteID, boardID, threadID, data) { - var count, div, excerpt, fullID, link, page, ref, title, x; + var count, div, excerpt, fullID, isArchived, link, page, ref, title, x; x = $.el('a', { className: 'fa fa-times', href: 'javascript:;' }); $.on(x, 'click', ThreadWatcher.cb.rm); - excerpt = data.excerpt; + excerpt = data.excerpt, isArchived = data.isArchived; excerpt || (excerpt = "/" + boardID + "/ - No." + threadID); if (Conf['Show Site Prefix']) { excerpt = ThreadWatcher.prefixes[siteID] + excerpt; @@ -22159,7 +22179,7 @@ ThreadWatcher = (function() { siteID: siteID, boardID: boardID, threadID: threadID - }) : void 0) || '', + }, isArchived) : void 0) || '', title: excerpt, className: 'watcher-link' }); @@ -22312,7 +22332,7 @@ ThreadWatcher = (function() { return; } if (newData.isDead || newData.last === -1) { - ref1 = ['page', 'lastPage', 'unread', 'quotingyou']; + ref1 = ['isArchived', 'page', 'lastPage', 'unread', 'quotingyou']; for (j = 0, len1 = ref1.length; j < len1; j++) { key = ref1[j]; if (!(key in newData)) { @@ -22362,7 +22382,7 @@ ThreadWatcher = (function() { }); return cb(); } - if (data.isDead && !((data.page != null) || (data.lastPage != null) || (data.unread != null) || (data.quotingYou != null))) { + if (data.isDead && !((data.isArchived != null) || (data.page != null) || (data.lastPage != null) || (data.unread != null) || (data.quotingYou != null))) { return cb(); } return ThreadWatcher.db.extend({ @@ -22370,6 +22390,7 @@ ThreadWatcher = (function() { threadID: threadID, val: { isDead: true, + isArchived: void 0, page: void 0, lastPage: void 0, unread: void 0, @@ -22905,6 +22926,7 @@ Unread = (function() { return ThreadWatcher.update(g.SITE.ID, Unread.thread.board.ID, Unread.thread.ID, { last: Unread.thread.lastPost, isDead: Unread.thread.isDead, + isArchived: Unread.thread.isArchived, unread: Unread.posts.size, quotingYou: quotingYou.last || 0 }); @@ -27460,6 +27482,10 @@ Main = (function() { } else if ((ref = pathname[2]) === 'thread' || ref === 'res') { r.VIEW = 'thread'; r.threadID = r.THREADID = +pathname[3].replace(/\.\w+$/, ''); + } else if (pathname[2] === 'archive' && pathname[3] === 'res') { + r.VIEW = 'thread'; + r.threadID = r.THREADID = +pathname[4].replace(/\.\w+$/, ''); + r.threadArchived = true; } else if (/^(?:catalog|archive)(?:\.\w+)?$/.test(pathname[2])) { r.VIEW = pathname[2].replace(/\.\w+$/, ''); } else if (/^(?:index|\d*)(?:\.\w+)?$/.test(pathname[2])) { @@ -27702,6 +27728,10 @@ Main = (function() { Main.handleErrors(errors); } if (g.VIEW === 'thread') { + if (g.threadArchived) { + threads[0].isArchived = true; + threads[0].kill(); + } if (typeof (base = g.SITE).parseThreadMetadata === "function") { base.parseThreadMetadata(threads[0]); } diff --git a/builds/4chan-X-noupdate.crx b/builds/4chan-X-noupdate.crx index ed9c1188d..ee741690c 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 b1cd3cc1e..48b736440 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.14.5 +// @version 1.14.15.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -211,7 +211,7 @@ docSet = function() { }; g = { - VERSION: '1.14.14.5', + VERSION: '1.14.15.0', NAMESPACE: '4chan X.', sites: Object.create(null), boards: Object.create(null) @@ -591,7 +591,8 @@ Config = (function() { knownBanners: '0.jpg,1.jpg,2.jpg,4.jpg,6.jpg,7.jpg,8.jpg,9.jpg,10.jpg,11.jpg,12.jpg,13.jpg,14.jpg,16.jpg,17.jpg,18.jpg,19.jpg,20.jpg,21.jpg,22.jpg,24.jpg,25.jpg,26.jpg,28.jpg,29.jpg,33.jpg,38.jpg,39.jpg,43.jpg,44.jpg,45.jpg,46.jpg,47.jpg,52.jpg,54.jpg,57.jpg,59.jpg,60.jpg,61.jpg,64.jpg,66.jpg,67.jpg,69.jpg,71.jpg,72.jpg,76.jpg,77.jpg,81.jpg,82.jpg,83.jpg,84.jpg,88.jpg,90.jpg,91.jpg,96.jpg,98.jpg,99.jpg,100.jpg,104.jpg,106.jpg,116.jpg,119.jpg,137.jpg,140.jpg,148.jpg,149.jpg,150.jpg,154.jpg,156.jpg,157.jpg,158.jpg,159.jpg,161.jpg,162.jpg,164.jpg,165.jpg,166.jpg,167.jpg,168.jpg,169.jpg,170.jpg,171.jpg,172.jpg,173.jpg,174.jpg,175.jpg,176.jpg,178.jpg,179.jpg,180.jpg,181.jpg,182.jpg,183.jpg,186.jpg,189.jpg,190.jpg,192.jpg,193.jpg,194.jpg,197.jpg,198.jpg,200.jpg,201.jpg,202.jpg,203.jpg,205.jpg,206.jpg,207.jpg,208.jpg,210.jpg,213.jpg,214.jpg,215.jpg,216.jpg,218.jpg,219.jpg,220.jpg,221.jpg,222.jpg,223.jpg,224.jpg,227.jpg,0.png,1.png,2.png,3.png,5.png,6.png,9.png,10.png,11.png,12.png,14.png,16.png,19.png,20.png,21.png,22.png,23.png,24.png,26.png,27.png,28.png,29.png,30.png,31.png,32.png,33.png,34.png,37.png,39.png,40.png,41.png,42.png,43.png,44.png,45.png,48.png,49.png,50.png,51.png,52.png,53.png,57.png,58.png,59.png,64.png,66.png,67.png,68.png,69.png,70.png,71.png,72.png,76.png,78.png,79.png,81.png,82.png,85.png,86.png,87.png,89.png,95.png,98.png,100.png,101.png,102.png,105.png,106.png,107.png,109.png,110.png,111.png,112.png,113.png,114.png,115.png,116.png,118.png,119.png,120.png,121.png,122.png,123.png,126.png,128.png,130.png,134.png,136.png,138.png,139.png,140.png,142.png,145.png,146.png,149.png,150.png,151.png,152.png,153.png,154.png,155.png,156.png,157.png,158.png,159.png,160.png,163.png,164.png,165.png,166.png,167.png,168.png,169.png,170.png,171.png,172.png,173.png,174.png,178.png,179.png,180.png,181.png,182.png,184.png,186.png,188.png,190.png,192.png,193.png,194.png,195.png,196.png,197.png,198.png,200.png,202.png,203.png,205.png,206.png,207.png,209.png,212.png,213.png,214.png,216.png,217.png,218.png,219.png,220.png,221.png,222.png,223.png,224.png,225.png,226.png,229.png,231.png,232.png,233.png,234.png,235.png,237.png,238.png,239.png,240.png,241.png,242.png,244.png,245.png,246.png,247.png,248.png,249.png,250.png,253.png,254.png,255.png,256.png,257.png,258.png,259.png,260.png,262.png,268.png,0.gif,1.gif,2.gif,3.gif,4.gif,5.gif,6.gif,7.gif,8.gif,9.gif,10.gif,12.gif,13.gif,14.gif,15.gif,16.gif,18.gif,19.gif,20.gif,21.gif,22.gif,23.gif,24.gif,28.gif,29.gif,30.gif,33.gif,34.gif,35.gif,36.gif,37.gif,39.gif,40.gif,42.gif,44.gif,45.gif,46.gif,48.gif,50.gif,52.gif,54.gif,55.gif,57.gif,58.gif,59.gif,60.gif,61.gif,63.gif,64.gif,66.gif,67.gif,68.gif,69.gif,70.gif,72.gif,73.gif,75.gif,76.gif,77.gif,78.gif,80.gif,81.gif,82.gif,83.gif,86.gif,87.gif,88.gif,92.gif,93.gif,94.gif,95.gif,96.gif,97.gif,98.gif,99.gif,100.gif,101.gif,102.gif,103.gif,104.gif,105.gif,106.gif,108.gif,109.gif,110.gif,111.gif,112.gif,113.gif,115.gif,116.gif,117.gif,118.gif,119.gif,120.gif,122.gif,123.gif,124.gif,127.gif,129.gif,130.gif,131.gif,134.gif,135.gif,136.gif,138.gif,139.gif,141.gif,144.gif,146.gif,148.gif,149.gif,153.gif,154.gif,155.gif,157.gif,158.gif,159.gif,160.gif,161.gif,162.gif,164.gif,166.gif,167.gif,168.gif,169.gif,170.gif,171.gif,172.gif,173.gif,174.gif,175.gif,176.gif,177.gif,178.gif,181.gif,182.gif,183.gif,185.gif,186.gif,187.gif,188.gif,189.gif,190.gif,191.gif,192.gif,193.gif,195.gif,196.gif,197.gif,200.gif,201.gif,202.gif,203.gif,204.gif,205.gif,206.gif,207.gif,208.gif,209.gif,210.gif,211.gif,212.gif,213.gif,214.gif,215.gif,216.gif,217.gif,219.gif,220.gif,221.gif,222.gif,224.gif,225.gif,226.gif,227.gif,228.gif,230.gif,232.gif,233.gif,234.gif,235.gif,238.gif,240.gif,241.gif,243.gif,244.gif,245.gif,246.gif,247.gif,249.gif,250.gif,251.gif,253.gif', cachedTitles: [[]], passMessageClosed: false, - 'Prerequest Captcha': false + 'Prerequest Captcha': false, + youtubeAPIKey: 'AIzaSyB5_zaen_-46Uhz1xGR-lz1YoUMHqCD6CE' }; return Config; @@ -6405,7 +6406,7 @@ DataBoard = (function() { } response1 = this.response; return $.cache(archiveList, function() { - if (this.status !== 200) { + if (!(this.status === 200 || (!g.SITE.archivedBoardsKnown && this.status === 404))) { return; } return that.ajaxCleanParse(boardID, response1, this.response); @@ -6887,7 +6888,9 @@ Post = (function() { ref = ['isSticky', 'isClosed', 'isArchived']; for (j = 0, len = ref.length; j < len; j++) { key = ref[j]; - this.thread[key] = (selector = g.SITE.selectors.icons[key]) ? !!$(selector, this.nodes.info) : false; + if ((selector = g.SITE.selectors.icons[key])) { + this.thread[key] = !!$(selector, this.nodes.info); + } } if (this.thread.isArchived) { this.thread.isClosed = true; @@ -7749,10 +7752,10 @@ SW = {}; return false; }, urls: { - thread: function(arg) { + thread: function(arg, isArchived) { var boardID, ref, siteID, threadID; siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID; - return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/res/" + threadID + ".html"; + return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/" + (isArchived ? 'archive/' : '') + "res/" + threadID + ".html"; }, post: function(arg) { var postID; @@ -7769,16 +7772,19 @@ SW = {}; siteID = arg.siteID, boardID = arg.boardID; return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/catalog.html"; }, - threadJSON: function(arg) { + threadJSON: function(arg, isArchived) { var boardID, ref, root, siteID, threadID; siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID; root = (ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0; if (root) { - return "" + root + boardID + "/res/" + threadID + ".json"; + return "" + root + boardID + "/" + (isArchived ? 'archive/' : '') + "res/" + threadID + ".json"; } else { return ''; } }, + archivedThreadJSON: function(thread) { + return SW.tinyboard.urls.threadJSON(thread, true); + }, threadsListJSON: function(arg) { var boardID, ref, root, siteID; siteID = arg.siteID, boardID = arg.boardID; @@ -7789,6 +7795,16 @@ SW = {}; return ''; } }, + archiveListJSON: function(arg) { + var boardID, ref, root, siteID; + siteID = arg.siteID, boardID = arg.boardID; + root = (ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0; + if (root) { + return "" + root + boardID + "/archive/archive.json"; + } else { + return ''; + } + }, catalogJSON: function(arg) { var boardID, ref, root, siteID; siteID = arg.siteID, boardID = arg.boardID; @@ -7991,6 +8007,7 @@ SW = {}; SW.yotsuba = { isOPContainerThread: false, hasIPCount: true, + archivedBoardsKnown: true, urls: { thread: function(arg) { var boardID, threadID; @@ -10817,7 +10834,7 @@ Header = (function() { } else { a = $.el('a', { href: "/" + g.BOARD.ID + "/", - textContent: text || g.BOARD.ID, + textContent: text || decodeURIComponent(g.BOARD.ID), className: 'current' }); if (/-nt/.test(t)) { @@ -13444,7 +13461,7 @@ Settings = (function() { }, advanced: function(section) { var applyCSS, boardSelect, customCSS, event, input, inputs, interval, items, itemsArchive, j, k, l, len, len1, len2, len3, listImageHost, m, name, ref, ref1, ref2, ref3, ref4, table, textContent, updateArchives, warning; - $.extend(section, {innerHTML: "
Archives
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection

Archive Lists: Each line below should be an archive list in this format or a URL to load an archive list from.
Archive properties can be overriden by another item with the same uid (or if absent, its name).
Last updated:
External Catalog
External Catalog is disabled. This will be used only as a fallback.
URLs of external catalog sites, where %board is to be replaced by the board name.
Each URL should be followed by ;boards: and optionally ;exclude: and a list of supported/excluded boards in the format explained in the Filter guide.
Override 4chan Image Host
Change 4chan image links to this domain. Leave blank for no change.
Captcha Language
Choose from list of language codes. Leave blank to autoselect.
Captcha Solving Service
Supported services include captcha.guru, 2captcha, and any other service implementing the 2captcha API.
Leave blank to disable.
Domain: API Key:
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
Index mode: g-mode:"infinite scrolling"
Index sort: g-sort:"creation date rev"
External link: external-text:"Google","http://www.google.com"
Open in new tab: g-nt
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:"Piracy"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Supported format specifiers:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Language tag:
Quote Backlinks formatting is disabled.
:
Default pasted content filename
.png
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (4chan filename)
Filename: %n (truncated), %N (untruncated), %t (4chan filename)
Download button: %d
Quick filter MD5: %f
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
Tag: %g
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input's auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
For more information about customizing 4chan X's CSS, see the styling guide.
Javascript Whitelist
Sources from which Javascript is allowed to be loaded by Content Security Policy.
Lines starting with a # will be ignored.
Known Banners
List of known banners, used for click-to-change feature.
"}); + $.extend(section, {innerHTML: "
Archives
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection

Archive Lists: Each line below should be an archive list in this format or a URL to load an archive list from.
Archive properties can be overriden by another item with the same uid (or if absent, its name).
Last updated:
External Catalog
External Catalog is disabled. This will be used only as a fallback.
URLs of external catalog sites, where %board is to be replaced by the board name.
Each URL should be followed by ;boards: and optionally ;exclude: and a list of supported/excluded boards in the format explained in the Filter guide.
Override 4chan Image Host
Change 4chan image links to this domain. Leave blank for no change.
Captcha Language
Choose from list of language codes. Leave blank to autoselect.
Captcha Solving Service
Supported services include captcha.guru, 2captcha, and any other service implementing the 2captcha API.
Leave blank to disable.
Domain: API Key:
Youtube API Key
API key used to fetch Youtube link titles. You can obtain your own key here to make the feature work when the default key exceeds its quota.
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
Index mode: g-mode:"infinite scrolling"
Index sort: g-sort:"creation date rev"
External link: external-text:"Google","http://www.google.com"
Open in new tab: g-nt
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:"Piracy"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Supported format specifiers:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Language tag:
Quote Backlinks formatting is disabled.
:
Default pasted content filename
.png
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (4chan filename)
Filename: %n (truncated), %N (untruncated), %t (4chan filename)
Download button: %d
Quick filter MD5: %f
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
Tag: %g
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input's auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
For more information about customizing 4chan X's CSS, see the styling guide.
Javascript Whitelist
Sources from which Javascript is allowed to be loaded by Content Security Policy.
Lines starting with a # will be ignored.
Known Banners
List of known banners, used for click-to-change feature.
"}); ref = $$('.warning', section); for (j = 0, len = ref.length; j < len; j++) { warning = ref[j]; @@ -14969,7 +14986,7 @@ ImageCommon = (function() { return g.SITE.software === 'yotsuba' && !ImageHost.test(file.src.split('/')[2]); }, error: function(file, post, fileObj, delay, cb) { - var base, redirect, src, threadJSON, timeoutID, url; + var base, parseJSON, redirect, src, threadJSON, timeoutID, url; src = fileObj.url.split('/'); url = null; if (g.SITE.software === 'yotsuba' && Conf['404 Redirect']) { @@ -15004,32 +15021,43 @@ ImageCommon = (function() { if (!threadJSON) { return; } - return $.ajax(threadJSON, { - onloadend: function() { - var i, len, postObj, ref, ref1; - if (this.status === 404) { + parseJSON = function(isArchiveURL) { + var archivedThreadJSON, base1, i, len, postObj, ref, ref1; + if (this.status === 404) { + if (!isArchiveURL && (archivedThreadJSON = typeof (base1 = g.SITE.urls).archivedThreadJSON === "function" ? base1.archivedThreadJSON(post) : void 0)) { + $.ajax(archivedThreadJSON, { + onloadend: function() { + return parseJSON.call(this, true); + } + }); + } else { post.kill(!post.isClone, fileObj.index); } - if (this.status !== 200) { - return redirect(); - } - ref = this.response.posts; - for (i = 0, len = ref.length; i < len; i++) { - postObj = ref[i]; - if (postObj.no === post.ID) { - break; - } - } - if (postObj.no !== post.ID) { - post.kill(); - return redirect(); - } else if (ref1 = fileObj.docIndex, indexOf.call(g.SITE.Build.parseJSON(postObj, post.board).filesDeleted, ref1) >= 0) { - post.kill(true); - return redirect(); - } else { - return url = fileObj.url; + } + if (this.status !== 200) { + return redirect(); + } + ref = this.response.posts; + for (i = 0, len = ref.length; i < len; i++) { + postObj = ref[i]; + if (postObj.no === post.ID) { + break; } } + if (postObj.no !== post.ID) { + post.kill(); + return redirect(); + } else if (ref1 = fileObj.docIndex, indexOf.call(g.SITE.Build.parseJSON(postObj, post.board).filesDeleted, ref1) >= 0) { + post.kill(true); + return redirect(); + } else { + return url = fileObj.url; + } + }; + return $.ajax(threadJSON, { + onloadend: function() { + return parseJSON.call(this); + } }); }, addControls: function(video) { @@ -16181,7 +16209,7 @@ Sauce = (function() { return file.url; }, IMG: function(post, file, ext) { - if (ext === 'gif' || ext === 'jpg' || ext === 'png') { + if (ext === 'gif' || ext === 'jpg' || ext === 'jpeg' || ext === 'png') { return file.url; } else { return file.thumbURL; @@ -16632,7 +16660,7 @@ Embedding = (function() { cachedTitles = arg.cachedTitles; sync(cachedTitles); try { - cachedTitles = newEntries.concat(cachedTitles).slice(-100); + cachedTitles = newEntries.concat(cachedTitles).slice(-1000); } catch (error) { cachedTitles = newEntries; } @@ -17224,7 +17252,7 @@ Embedding = (function() { api: function(uids) { var ids, key; ids = encodeURIComponent(uids.join(',')); - key = 'AIzaSyB5_zaen_-46Uhz1xGR-lz1YoUMHqCD6CE'; + key = Conf['youtubeAPIKey']; return "https://www.googleapis.com/youtube/v3/videos?part=snippet&id=" + ids + "&fields=items%28id%2Csnippet%28title%29%29&key=" + key; }, text: function(data, uid) { @@ -21703,26 +21731,13 @@ ThreadWatcher = (function() { threadID: threadID }); nKilled++; - } else if (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) { + } else { ThreadWatcher.fetchStatus({ siteID: siteID, boardID: boardID, threadID: threadID, data: data }); - } else { - db.extend({ - boardID: boardID, - threadID: threadID, - val: { - isDead: true, - page: void 0, - lastPage: void 0, - unread: void 0, - quotingYou: void 0 - } - }); - nKilled++; } } if (nKilled) { @@ -21985,13 +22000,7 @@ ThreadWatcher = (function() { } } } else { - if (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) { - ThreadWatcher.fetchStatus(thread); - } else { - ThreadWatcher.update(siteID, boardID, threadID, { - isDead: true - }); - } + ThreadWatcher.fetchStatus(thread); } } }, @@ -22017,19 +22026,19 @@ ThreadWatcher = (function() { force: force }, [thread], ThreadWatcher.parseStatus); }, - parseStatus: function(arg) { - var boardID, data, isDead, j, last, lastReadPost, len1, match, newData, postObj, quotesYou, quotingYou, ref, ref1, ref2, regexp, replies, site, siteID, threadID, unread, youOP; - siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID, data = arg.data, newData = arg.newData; + parseStatus: function(thread, isArchiveURL) { + var archiveURL, base, boardID, data, force, isArchived, isDead, j, last, lastReadPost, len1, match, newData, postObj, quotesYou, quotingYou, ref, ref1, ref2, ref3, regexp, replies, site, siteID, threadID, unread, youOP; + siteID = thread.siteID, boardID = thread.boardID, threadID = thread.threadID, data = thread.data, newData = thread.newData, force = thread.force; site = g.sites[siteID]; if (this.status === 200 && this.response) { last = this.response.posts[this.response.posts.length - 1].no; replies = this.response.posts.length - 1; - isDead = !!this.response.posts[0].archived; + isDead = isArchived = !!(this.response.posts[0].archived || isArchiveURL); if (isDead && Conf['Auto Prune']) { ThreadWatcher.rm(siteID, boardID, threadID); return; } - if (last === data.last && isDead === data.isDead) { + if (last === data.last && isDead === data.isDead && isArchived === data.isArchived) { return; } lastReadPost = ThreadWatcher.unreaddb.get({ @@ -22096,12 +22105,23 @@ ThreadWatcher = (function() { last: last, replies: replies, isDead: isDead, + isArchived: isArchived, unread: unread, quotingYou: quotingYou }); return ThreadWatcher.update(siteID, boardID, threadID, newData); } else if (this.status === 404) { - if (site.mayLackJSON && (data.last == null)) { + archiveURL = (ref3 = g.sites[siteID]) != null ? typeof (base = ref3.urls).archivedThreadJSON === "function" ? base.archivedThreadJSON({ + siteID: siteID, + boardID: boardID, + threadID: threadID + }) : void 0 : void 0; + if (!isArchiveURL && archiveURL) { + return ThreadWatcher.fetch(archiveURL, { + siteID: siteID, + force: force + }, [thread, true], ThreadWatcher.parseStatus); + } else if (site.mayLackJSON && (data.last == null)) { return ThreadWatcher.update(siteID, boardID, threadID, { last: -1 }); @@ -22143,13 +22163,13 @@ ThreadWatcher = (function() { return all; }, makeLine: function(siteID, boardID, threadID, data) { - var count, div, excerpt, fullID, link, page, ref, title, x; + var count, div, excerpt, fullID, isArchived, link, page, ref, title, x; x = $.el('a', { className: 'fa fa-times', href: 'javascript:;' }); $.on(x, 'click', ThreadWatcher.cb.rm); - excerpt = data.excerpt; + excerpt = data.excerpt, isArchived = data.isArchived; excerpt || (excerpt = "/" + boardID + "/ - No." + threadID); if (Conf['Show Site Prefix']) { excerpt = ThreadWatcher.prefixes[siteID] + excerpt; @@ -22159,7 +22179,7 @@ ThreadWatcher = (function() { siteID: siteID, boardID: boardID, threadID: threadID - }) : void 0) || '', + }, isArchived) : void 0) || '', title: excerpt, className: 'watcher-link' }); @@ -22312,7 +22332,7 @@ ThreadWatcher = (function() { return; } if (newData.isDead || newData.last === -1) { - ref1 = ['page', 'lastPage', 'unread', 'quotingyou']; + ref1 = ['isArchived', 'page', 'lastPage', 'unread', 'quotingyou']; for (j = 0, len1 = ref1.length; j < len1; j++) { key = ref1[j]; if (!(key in newData)) { @@ -22362,7 +22382,7 @@ ThreadWatcher = (function() { }); return cb(); } - if (data.isDead && !((data.page != null) || (data.lastPage != null) || (data.unread != null) || (data.quotingYou != null))) { + if (data.isDead && !((data.isArchived != null) || (data.page != null) || (data.lastPage != null) || (data.unread != null) || (data.quotingYou != null))) { return cb(); } return ThreadWatcher.db.extend({ @@ -22370,6 +22390,7 @@ ThreadWatcher = (function() { threadID: threadID, val: { isDead: true, + isArchived: void 0, page: void 0, lastPage: void 0, unread: void 0, @@ -22905,6 +22926,7 @@ Unread = (function() { return ThreadWatcher.update(g.SITE.ID, Unread.thread.board.ID, Unread.thread.ID, { last: Unread.thread.lastPost, isDead: Unread.thread.isDead, + isArchived: Unread.thread.isArchived, unread: Unread.posts.size, quotingYou: quotingYou.last || 0 }); @@ -27460,6 +27482,10 @@ Main = (function() { } else if ((ref = pathname[2]) === 'thread' || ref === 'res') { r.VIEW = 'thread'; r.threadID = r.THREADID = +pathname[3].replace(/\.\w+$/, ''); + } else if (pathname[2] === 'archive' && pathname[3] === 'res') { + r.VIEW = 'thread'; + r.threadID = r.THREADID = +pathname[4].replace(/\.\w+$/, ''); + r.threadArchived = true; } else if (/^(?:catalog|archive)(?:\.\w+)?$/.test(pathname[2])) { r.VIEW = pathname[2].replace(/\.\w+$/, ''); } else if (/^(?:index|\d*)(?:\.\w+)?$/.test(pathname[2])) { @@ -27702,6 +27728,10 @@ Main = (function() { Main.handleErrors(errors); } if (g.VIEW === 'thread') { + if (g.threadArchived) { + threads[0].isArchived = true; + threads[0].kill(); + } if (typeof (base = g.SITE).parseThreadMetadata === "function") { base.parseThreadMetadata(threads[0]); } diff --git a/builds/4chan-X.crx b/builds/4chan-X.crx index c36a88eb7..b3a78f0d8 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 2ce3a5627..c685685cd 100644 --- a/builds/4chan-X.meta.js +++ b/builds/4chan-X.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.14.14.5 +// @version 1.14.15.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index cd4e8d90f..04747c278 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.14.14.5 +// @version 1.14.15.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -211,7 +211,7 @@ docSet = function() { }; g = { - VERSION: '1.14.14.5', + VERSION: '1.14.15.0', NAMESPACE: '4chan X.', sites: Object.create(null), boards: Object.create(null) @@ -591,7 +591,8 @@ Config = (function() { knownBanners: '0.jpg,1.jpg,2.jpg,4.jpg,6.jpg,7.jpg,8.jpg,9.jpg,10.jpg,11.jpg,12.jpg,13.jpg,14.jpg,16.jpg,17.jpg,18.jpg,19.jpg,20.jpg,21.jpg,22.jpg,24.jpg,25.jpg,26.jpg,28.jpg,29.jpg,33.jpg,38.jpg,39.jpg,43.jpg,44.jpg,45.jpg,46.jpg,47.jpg,52.jpg,54.jpg,57.jpg,59.jpg,60.jpg,61.jpg,64.jpg,66.jpg,67.jpg,69.jpg,71.jpg,72.jpg,76.jpg,77.jpg,81.jpg,82.jpg,83.jpg,84.jpg,88.jpg,90.jpg,91.jpg,96.jpg,98.jpg,99.jpg,100.jpg,104.jpg,106.jpg,116.jpg,119.jpg,137.jpg,140.jpg,148.jpg,149.jpg,150.jpg,154.jpg,156.jpg,157.jpg,158.jpg,159.jpg,161.jpg,162.jpg,164.jpg,165.jpg,166.jpg,167.jpg,168.jpg,169.jpg,170.jpg,171.jpg,172.jpg,173.jpg,174.jpg,175.jpg,176.jpg,178.jpg,179.jpg,180.jpg,181.jpg,182.jpg,183.jpg,186.jpg,189.jpg,190.jpg,192.jpg,193.jpg,194.jpg,197.jpg,198.jpg,200.jpg,201.jpg,202.jpg,203.jpg,205.jpg,206.jpg,207.jpg,208.jpg,210.jpg,213.jpg,214.jpg,215.jpg,216.jpg,218.jpg,219.jpg,220.jpg,221.jpg,222.jpg,223.jpg,224.jpg,227.jpg,0.png,1.png,2.png,3.png,5.png,6.png,9.png,10.png,11.png,12.png,14.png,16.png,19.png,20.png,21.png,22.png,23.png,24.png,26.png,27.png,28.png,29.png,30.png,31.png,32.png,33.png,34.png,37.png,39.png,40.png,41.png,42.png,43.png,44.png,45.png,48.png,49.png,50.png,51.png,52.png,53.png,57.png,58.png,59.png,64.png,66.png,67.png,68.png,69.png,70.png,71.png,72.png,76.png,78.png,79.png,81.png,82.png,85.png,86.png,87.png,89.png,95.png,98.png,100.png,101.png,102.png,105.png,106.png,107.png,109.png,110.png,111.png,112.png,113.png,114.png,115.png,116.png,118.png,119.png,120.png,121.png,122.png,123.png,126.png,128.png,130.png,134.png,136.png,138.png,139.png,140.png,142.png,145.png,146.png,149.png,150.png,151.png,152.png,153.png,154.png,155.png,156.png,157.png,158.png,159.png,160.png,163.png,164.png,165.png,166.png,167.png,168.png,169.png,170.png,171.png,172.png,173.png,174.png,178.png,179.png,180.png,181.png,182.png,184.png,186.png,188.png,190.png,192.png,193.png,194.png,195.png,196.png,197.png,198.png,200.png,202.png,203.png,205.png,206.png,207.png,209.png,212.png,213.png,214.png,216.png,217.png,218.png,219.png,220.png,221.png,222.png,223.png,224.png,225.png,226.png,229.png,231.png,232.png,233.png,234.png,235.png,237.png,238.png,239.png,240.png,241.png,242.png,244.png,245.png,246.png,247.png,248.png,249.png,250.png,253.png,254.png,255.png,256.png,257.png,258.png,259.png,260.png,262.png,268.png,0.gif,1.gif,2.gif,3.gif,4.gif,5.gif,6.gif,7.gif,8.gif,9.gif,10.gif,12.gif,13.gif,14.gif,15.gif,16.gif,18.gif,19.gif,20.gif,21.gif,22.gif,23.gif,24.gif,28.gif,29.gif,30.gif,33.gif,34.gif,35.gif,36.gif,37.gif,39.gif,40.gif,42.gif,44.gif,45.gif,46.gif,48.gif,50.gif,52.gif,54.gif,55.gif,57.gif,58.gif,59.gif,60.gif,61.gif,63.gif,64.gif,66.gif,67.gif,68.gif,69.gif,70.gif,72.gif,73.gif,75.gif,76.gif,77.gif,78.gif,80.gif,81.gif,82.gif,83.gif,86.gif,87.gif,88.gif,92.gif,93.gif,94.gif,95.gif,96.gif,97.gif,98.gif,99.gif,100.gif,101.gif,102.gif,103.gif,104.gif,105.gif,106.gif,108.gif,109.gif,110.gif,111.gif,112.gif,113.gif,115.gif,116.gif,117.gif,118.gif,119.gif,120.gif,122.gif,123.gif,124.gif,127.gif,129.gif,130.gif,131.gif,134.gif,135.gif,136.gif,138.gif,139.gif,141.gif,144.gif,146.gif,148.gif,149.gif,153.gif,154.gif,155.gif,157.gif,158.gif,159.gif,160.gif,161.gif,162.gif,164.gif,166.gif,167.gif,168.gif,169.gif,170.gif,171.gif,172.gif,173.gif,174.gif,175.gif,176.gif,177.gif,178.gif,181.gif,182.gif,183.gif,185.gif,186.gif,187.gif,188.gif,189.gif,190.gif,191.gif,192.gif,193.gif,195.gif,196.gif,197.gif,200.gif,201.gif,202.gif,203.gif,204.gif,205.gif,206.gif,207.gif,208.gif,209.gif,210.gif,211.gif,212.gif,213.gif,214.gif,215.gif,216.gif,217.gif,219.gif,220.gif,221.gif,222.gif,224.gif,225.gif,226.gif,227.gif,228.gif,230.gif,232.gif,233.gif,234.gif,235.gif,238.gif,240.gif,241.gif,243.gif,244.gif,245.gif,246.gif,247.gif,249.gif,250.gif,251.gif,253.gif', cachedTitles: [[]], passMessageClosed: false, - 'Prerequest Captcha': false + 'Prerequest Captcha': false, + youtubeAPIKey: 'AIzaSyB5_zaen_-46Uhz1xGR-lz1YoUMHqCD6CE' }; return Config; @@ -6405,7 +6406,7 @@ DataBoard = (function() { } response1 = this.response; return $.cache(archiveList, function() { - if (this.status !== 200) { + if (!(this.status === 200 || (!g.SITE.archivedBoardsKnown && this.status === 404))) { return; } return that.ajaxCleanParse(boardID, response1, this.response); @@ -6887,7 +6888,9 @@ Post = (function() { ref = ['isSticky', 'isClosed', 'isArchived']; for (j = 0, len = ref.length; j < len; j++) { key = ref[j]; - this.thread[key] = (selector = g.SITE.selectors.icons[key]) ? !!$(selector, this.nodes.info) : false; + if ((selector = g.SITE.selectors.icons[key])) { + this.thread[key] = !!$(selector, this.nodes.info); + } } if (this.thread.isArchived) { this.thread.isClosed = true; @@ -7749,10 +7752,10 @@ SW = {}; return false; }, urls: { - thread: function(arg) { + thread: function(arg, isArchived) { var boardID, ref, siteID, threadID; siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID; - return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/res/" + threadID + ".html"; + return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/" + (isArchived ? 'archive/' : '') + "res/" + threadID + ".html"; }, post: function(arg) { var postID; @@ -7769,16 +7772,19 @@ SW = {}; siteID = arg.siteID, boardID = arg.boardID; return "" + (((ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0) || ("http://" + siteID + "/")) + boardID + "/catalog.html"; }, - threadJSON: function(arg) { + threadJSON: function(arg, isArchived) { var boardID, ref, root, siteID, threadID; siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID; root = (ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0; if (root) { - return "" + root + boardID + "/res/" + threadID + ".json"; + return "" + root + boardID + "/" + (isArchived ? 'archive/' : '') + "res/" + threadID + ".json"; } else { return ''; } }, + archivedThreadJSON: function(thread) { + return SW.tinyboard.urls.threadJSON(thread, true); + }, threadsListJSON: function(arg) { var boardID, ref, root, siteID; siteID = arg.siteID, boardID = arg.boardID; @@ -7789,6 +7795,16 @@ SW = {}; return ''; } }, + archiveListJSON: function(arg) { + var boardID, ref, root, siteID; + siteID = arg.siteID, boardID = arg.boardID; + root = (ref = Conf['siteProperties'][siteID]) != null ? ref.root : void 0; + if (root) { + return "" + root + boardID + "/archive/archive.json"; + } else { + return ''; + } + }, catalogJSON: function(arg) { var boardID, ref, root, siteID; siteID = arg.siteID, boardID = arg.boardID; @@ -7991,6 +8007,7 @@ SW = {}; SW.yotsuba = { isOPContainerThread: false, hasIPCount: true, + archivedBoardsKnown: true, urls: { thread: function(arg) { var boardID, threadID; @@ -10817,7 +10834,7 @@ Header = (function() { } else { a = $.el('a', { href: "/" + g.BOARD.ID + "/", - textContent: text || g.BOARD.ID, + textContent: text || decodeURIComponent(g.BOARD.ID), className: 'current' }); if (/-nt/.test(t)) { @@ -13444,7 +13461,7 @@ Settings = (function() { }, advanced: function(section) { var applyCSS, boardSelect, customCSS, event, input, inputs, interval, items, itemsArchive, j, k, l, len, len1, len2, len3, listImageHost, m, name, ref, ref1, ref2, ref3, ref4, table, textContent, updateArchives, warning; - $.extend(section, {innerHTML: "
Archives
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection

Archive Lists: Each line below should be an archive list in this format or a URL to load an archive list from.
Archive properties can be overriden by another item with the same uid (or if absent, its name).
Last updated:
External Catalog
External Catalog is disabled. This will be used only as a fallback.
URLs of external catalog sites, where %board is to be replaced by the board name.
Each URL should be followed by ;boards: and optionally ;exclude: and a list of supported/excluded boards in the format explained in the Filter guide.
Override 4chan Image Host
Change 4chan image links to this domain. Leave blank for no change.
Captcha Language
Choose from list of language codes. Leave blank to autoselect.
Captcha Solving Service
Supported services include captcha.guru, 2captcha, and any other service implementing the 2captcha API.
Leave blank to disable.
Domain: API Key:
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
Index mode: g-mode:"infinite scrolling"
Index sort: g-sort:"creation date rev"
External link: external-text:"Google","http://www.google.com"
Open in new tab: g-nt
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:"Piracy"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Supported format specifiers:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Language tag:
Quote Backlinks formatting is disabled.
:
Default pasted content filename
.png
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (4chan filename)
Filename: %n (truncated), %N (untruncated), %t (4chan filename)
Download button: %d
Quick filter MD5: %f
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
Tag: %g
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input's auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
For more information about customizing 4chan X's CSS, see the styling guide.
Javascript Whitelist
Sources from which Javascript is allowed to be loaded by Content Security Policy.
Lines starting with a # will be ignored.
Known Banners
List of known banners, used for click-to-change feature.
"}); + $.extend(section, {innerHTML: "
Archives
404 Redirect is disabled.
Thread redirectionPost fetchingFile redirection

Archive Lists: Each line below should be an archive list in this format or a URL to load an archive list from.
Archive properties can be overriden by another item with the same uid (or if absent, its name).
Last updated:
External Catalog
External Catalog is disabled. This will be used only as a fallback.
URLs of external catalog sites, where %board is to be replaced by the board name.
Each URL should be followed by ;boards: and optionally ;exclude: and a list of supported/excluded boards in the format explained in the Filter guide.
Override 4chan Image Host
Change 4chan image links to this domain. Leave blank for no change.
Captcha Language
Choose from list of language codes. Leave blank to autoselect.
Captcha Solving Service
Supported services include captcha.guru, 2captcha, and any other service implementing the 2captcha API.
Leave blank to disable.
Domain: API Key:
Youtube API Key
API key used to fetch Youtube link titles. You can obtain your own key here to make the feature work when the default key exceeds its quota.
Custom Board Navigation
New lines will be converted into spaces.

In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
Board link: g
Archive link: g-archive
Internal archive link: g-expired
Title link: g-title
Board link (Replace with title when on that board): g-replace
Full text link: g-full
Custom text link: g-text:"Install Gentoo"
Index-only link: g-index
Catalog-only link: g-catalog
Index mode: g-mode:"infinite scrolling"
Index sort: g-sort:"creation date rev"
External link: external-text:"Google","http://www.google.com"
Open in new tab: g-nt
Combinations are possible: g-index-text:"Technology Index"
Full board list toggle: toggle-all

[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h] [t-text:"Piracy"]
will give you
[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
if you are on /g/.
Time Formatting is disabled.
:
Supported format specifiers:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
Language tag:
Quote Backlinks formatting is disabled.
:
Default pasted content filename
.png
File Info Formatting is disabled.
:
Link: %l (truncated), %L (untruncated), %T (4chan filename)
Filename: %n (truncated), %N (untruncated), %t (4chan filename)
Download button: %d
Quick filter MD5: %f
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
Tag: %g
Literal %: %%
Quick Reply Personas

One item per line.
Items will be added in the relevant input's auto-completion list.
Password items will always be used, since there is no password input.
Lines starting with a # will be ignored.

Unread Favicon is disabled.
Thread Updater is disabled.
Interval: seconds
Custom Cooldown Time
Seconds:
For more information about customizing 4chan X's CSS, see the styling guide.
Javascript Whitelist
Sources from which Javascript is allowed to be loaded by Content Security Policy.
Lines starting with a # will be ignored.
Known Banners
List of known banners, used for click-to-change feature.
"}); ref = $$('.warning', section); for (j = 0, len = ref.length; j < len; j++) { warning = ref[j]; @@ -14969,7 +14986,7 @@ ImageCommon = (function() { return g.SITE.software === 'yotsuba' && !ImageHost.test(file.src.split('/')[2]); }, error: function(file, post, fileObj, delay, cb) { - var base, redirect, src, threadJSON, timeoutID, url; + var base, parseJSON, redirect, src, threadJSON, timeoutID, url; src = fileObj.url.split('/'); url = null; if (g.SITE.software === 'yotsuba' && Conf['404 Redirect']) { @@ -15004,32 +15021,43 @@ ImageCommon = (function() { if (!threadJSON) { return; } - return $.ajax(threadJSON, { - onloadend: function() { - var i, len, postObj, ref, ref1; - if (this.status === 404) { + parseJSON = function(isArchiveURL) { + var archivedThreadJSON, base1, i, len, postObj, ref, ref1; + if (this.status === 404) { + if (!isArchiveURL && (archivedThreadJSON = typeof (base1 = g.SITE.urls).archivedThreadJSON === "function" ? base1.archivedThreadJSON(post) : void 0)) { + $.ajax(archivedThreadJSON, { + onloadend: function() { + return parseJSON.call(this, true); + } + }); + } else { post.kill(!post.isClone, fileObj.index); } - if (this.status !== 200) { - return redirect(); - } - ref = this.response.posts; - for (i = 0, len = ref.length; i < len; i++) { - postObj = ref[i]; - if (postObj.no === post.ID) { - break; - } - } - if (postObj.no !== post.ID) { - post.kill(); - return redirect(); - } else if (ref1 = fileObj.docIndex, indexOf.call(g.SITE.Build.parseJSON(postObj, post.board).filesDeleted, ref1) >= 0) { - post.kill(true); - return redirect(); - } else { - return url = fileObj.url; + } + if (this.status !== 200) { + return redirect(); + } + ref = this.response.posts; + for (i = 0, len = ref.length; i < len; i++) { + postObj = ref[i]; + if (postObj.no === post.ID) { + break; } } + if (postObj.no !== post.ID) { + post.kill(); + return redirect(); + } else if (ref1 = fileObj.docIndex, indexOf.call(g.SITE.Build.parseJSON(postObj, post.board).filesDeleted, ref1) >= 0) { + post.kill(true); + return redirect(); + } else { + return url = fileObj.url; + } + }; + return $.ajax(threadJSON, { + onloadend: function() { + return parseJSON.call(this); + } }); }, addControls: function(video) { @@ -16181,7 +16209,7 @@ Sauce = (function() { return file.url; }, IMG: function(post, file, ext) { - if (ext === 'gif' || ext === 'jpg' || ext === 'png') { + if (ext === 'gif' || ext === 'jpg' || ext === 'jpeg' || ext === 'png') { return file.url; } else { return file.thumbURL; @@ -16632,7 +16660,7 @@ Embedding = (function() { cachedTitles = arg.cachedTitles; sync(cachedTitles); try { - cachedTitles = newEntries.concat(cachedTitles).slice(-100); + cachedTitles = newEntries.concat(cachedTitles).slice(-1000); } catch (error) { cachedTitles = newEntries; } @@ -17224,7 +17252,7 @@ Embedding = (function() { api: function(uids) { var ids, key; ids = encodeURIComponent(uids.join(',')); - key = 'AIzaSyB5_zaen_-46Uhz1xGR-lz1YoUMHqCD6CE'; + key = Conf['youtubeAPIKey']; return "https://www.googleapis.com/youtube/v3/videos?part=snippet&id=" + ids + "&fields=items%28id%2Csnippet%28title%29%29&key=" + key; }, text: function(data, uid) { @@ -21703,26 +21731,13 @@ ThreadWatcher = (function() { threadID: threadID }); nKilled++; - } else if (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) { + } else { ThreadWatcher.fetchStatus({ siteID: siteID, boardID: boardID, threadID: threadID, data: data }); - } else { - db.extend({ - boardID: boardID, - threadID: threadID, - val: { - isDead: true, - page: void 0, - lastPage: void 0, - unread: void 0, - quotingYou: void 0 - } - }); - nKilled++; } } if (nKilled) { @@ -21985,13 +22000,7 @@ ThreadWatcher = (function() { } } } else { - if (ThreadWatcher.unreadEnabled && Conf['Show Unread Count']) { - ThreadWatcher.fetchStatus(thread); - } else { - ThreadWatcher.update(siteID, boardID, threadID, { - isDead: true - }); - } + ThreadWatcher.fetchStatus(thread); } } }, @@ -22017,19 +22026,19 @@ ThreadWatcher = (function() { force: force }, [thread], ThreadWatcher.parseStatus); }, - parseStatus: function(arg) { - var boardID, data, isDead, j, last, lastReadPost, len1, match, newData, postObj, quotesYou, quotingYou, ref, ref1, ref2, regexp, replies, site, siteID, threadID, unread, youOP; - siteID = arg.siteID, boardID = arg.boardID, threadID = arg.threadID, data = arg.data, newData = arg.newData; + parseStatus: function(thread, isArchiveURL) { + var archiveURL, base, boardID, data, force, isArchived, isDead, j, last, lastReadPost, len1, match, newData, postObj, quotesYou, quotingYou, ref, ref1, ref2, ref3, regexp, replies, site, siteID, threadID, unread, youOP; + siteID = thread.siteID, boardID = thread.boardID, threadID = thread.threadID, data = thread.data, newData = thread.newData, force = thread.force; site = g.sites[siteID]; if (this.status === 200 && this.response) { last = this.response.posts[this.response.posts.length - 1].no; replies = this.response.posts.length - 1; - isDead = !!this.response.posts[0].archived; + isDead = isArchived = !!(this.response.posts[0].archived || isArchiveURL); if (isDead && Conf['Auto Prune']) { ThreadWatcher.rm(siteID, boardID, threadID); return; } - if (last === data.last && isDead === data.isDead) { + if (last === data.last && isDead === data.isDead && isArchived === data.isArchived) { return; } lastReadPost = ThreadWatcher.unreaddb.get({ @@ -22096,12 +22105,23 @@ ThreadWatcher = (function() { last: last, replies: replies, isDead: isDead, + isArchived: isArchived, unread: unread, quotingYou: quotingYou }); return ThreadWatcher.update(siteID, boardID, threadID, newData); } else if (this.status === 404) { - if (site.mayLackJSON && (data.last == null)) { + archiveURL = (ref3 = g.sites[siteID]) != null ? typeof (base = ref3.urls).archivedThreadJSON === "function" ? base.archivedThreadJSON({ + siteID: siteID, + boardID: boardID, + threadID: threadID + }) : void 0 : void 0; + if (!isArchiveURL && archiveURL) { + return ThreadWatcher.fetch(archiveURL, { + siteID: siteID, + force: force + }, [thread, true], ThreadWatcher.parseStatus); + } else if (site.mayLackJSON && (data.last == null)) { return ThreadWatcher.update(siteID, boardID, threadID, { last: -1 }); @@ -22143,13 +22163,13 @@ ThreadWatcher = (function() { return all; }, makeLine: function(siteID, boardID, threadID, data) { - var count, div, excerpt, fullID, link, page, ref, title, x; + var count, div, excerpt, fullID, isArchived, link, page, ref, title, x; x = $.el('a', { className: 'fa fa-times', href: 'javascript:;' }); $.on(x, 'click', ThreadWatcher.cb.rm); - excerpt = data.excerpt; + excerpt = data.excerpt, isArchived = data.isArchived; excerpt || (excerpt = "/" + boardID + "/ - No." + threadID); if (Conf['Show Site Prefix']) { excerpt = ThreadWatcher.prefixes[siteID] + excerpt; @@ -22159,7 +22179,7 @@ ThreadWatcher = (function() { siteID: siteID, boardID: boardID, threadID: threadID - }) : void 0) || '', + }, isArchived) : void 0) || '', title: excerpt, className: 'watcher-link' }); @@ -22312,7 +22332,7 @@ ThreadWatcher = (function() { return; } if (newData.isDead || newData.last === -1) { - ref1 = ['page', 'lastPage', 'unread', 'quotingyou']; + ref1 = ['isArchived', 'page', 'lastPage', 'unread', 'quotingyou']; for (j = 0, len1 = ref1.length; j < len1; j++) { key = ref1[j]; if (!(key in newData)) { @@ -22362,7 +22382,7 @@ ThreadWatcher = (function() { }); return cb(); } - if (data.isDead && !((data.page != null) || (data.lastPage != null) || (data.unread != null) || (data.quotingYou != null))) { + if (data.isDead && !((data.isArchived != null) || (data.page != null) || (data.lastPage != null) || (data.unread != null) || (data.quotingYou != null))) { return cb(); } return ThreadWatcher.db.extend({ @@ -22370,6 +22390,7 @@ ThreadWatcher = (function() { threadID: threadID, val: { isDead: true, + isArchived: void 0, page: void 0, lastPage: void 0, unread: void 0, @@ -22905,6 +22926,7 @@ Unread = (function() { return ThreadWatcher.update(g.SITE.ID, Unread.thread.board.ID, Unread.thread.ID, { last: Unread.thread.lastPost, isDead: Unread.thread.isDead, + isArchived: Unread.thread.isArchived, unread: Unread.posts.size, quotingYou: quotingYou.last || 0 }); @@ -27460,6 +27482,10 @@ Main = (function() { } else if ((ref = pathname[2]) === 'thread' || ref === 'res') { r.VIEW = 'thread'; r.threadID = r.THREADID = +pathname[3].replace(/\.\w+$/, ''); + } else if (pathname[2] === 'archive' && pathname[3] === 'res') { + r.VIEW = 'thread'; + r.threadID = r.THREADID = +pathname[4].replace(/\.\w+$/, ''); + r.threadArchived = true; } else if (/^(?:catalog|archive)(?:\.\w+)?$/.test(pathname[2])) { r.VIEW = pathname[2].replace(/\.\w+$/, ''); } else if (/^(?:index|\d*)(?:\.\w+)?$/.test(pathname[2])) { @@ -27702,6 +27728,10 @@ Main = (function() { Main.handleErrors(errors); } if (g.VIEW === 'thread') { + if (g.threadArchived) { + threads[0].isArchived = true; + threads[0].kill(); + } if (typeof (base = g.SITE).parseThreadMetadata === "function") { base.parseThreadMetadata(threads[0]); } diff --git a/builds/4chan-X.zip b/builds/4chan-X.zip index d3f22d92a..08704591d 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 f9be01434..b5acb9ef5 100644 --- a/builds/updates-beta.json +++ b/builds/updates-beta.json @@ -3,7 +3,7 @@ "4chan-x@4chan-x.net": { "updates": [ { - "version": "1.14.14.5", + "version": "1.14.15.0", "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 3becd70e8..5a518709b 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 18a160c73..fae82d502 100644 --- a/builds/updates.json +++ b/builds/updates.json @@ -3,7 +3,7 @@ "4chan-x@4chan-x.net": { "updates": [ { - "version": "1.14.14.5", + "version": "1.14.15.0", "update_link": "https://www.4chan-x.net/builds/4chan-X.crx" } ] diff --git a/builds/updates.xml b/builds/updates.xml index 0dcc90a63..116f41732 100644 --- a/builds/updates.xml +++ b/builds/updates.xml @@ -1,7 +1,7 @@ - + diff --git a/version.json b/version.json index 4e41b8105..a981dfcdc 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { - "version": "1.14.14.5", - "date": "2019-12-13T09:22:10.108Z" + "version": "1.14.15.0", + "date": "2019-12-13T21:49:13.339Z" } \ No newline at end of file