diff --git a/CHANGELOG.md b/CHANGELOG.md index e462f6e38..4df8e2690 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ -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.12 + +**v1.14.12.0** *(2019-08-05)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.12.0/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.12.0/builds/4chan-X-noupdate.crx)] +- Based on v1.14.11.1. +- Shift+click on the Quick Reply submit button will now attempt to post regardless of cooldowns or other errors. +- Improved error reporting. #862 +- Detect conflicts with native extension and show appropriate error message. #1627 +- Only show MD5 quick filter notification when using keybind. #2385 +- Update Loopvid embedding. +- Update settings import from loadletter fork. +- (saxamaphone69) Make text areas in settings resize vertically only. +- Fix cooldown when the time is an exact number of minutes. #2301 +- Cache titles to reduce title requests. #2327 +- Other bugfixes. + ### v1.14.11 **v1.14.11.1** *(2019-08-03)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.11.1/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.14.11.1/builds/4chan-X-noupdate.crx)] diff --git a/builds/4chan-X-beta.crx b/builds/4chan-X-beta.crx index 3968fee23..c68e0e50d 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 8f13c6570..fe49197ab 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.11.1 +// @version 1.14.12.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 772067743..afda39109 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.11.1 +// @version 1.14.12.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -199,7 +199,7 @@ docSet = function() { }; g = { - VERSION: '1.14.11.1', + VERSION: '1.14.12.0', NAMESPACE: '4chan X.', sites: Object.create(null), boards: Object.create(null) @@ -575,7 +575,8 @@ Config = (function() { ], fourchanImageHost: 'i.4cdn.org', hiddenPSAList: [{}], - 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' + 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: [[]] }; return Config; @@ -2017,8 +2018,8 @@ div[data-checked=\"false\"] > .suboption-list {\n\ }\n\ #fourchanx-settings textarea {\n\ font-family: monospace;\n\ - min-width: 100%;\n\ - max-width: 100%;\n\ + width: 100%;\n\ + resize: vertical;\n\ }\n\ #fourchanx-settings code {\n\ color: #000;\n\ @@ -5247,23 +5248,23 @@ $ = (function() { } })(); - $.globalEval = function(code, data) { - var script; - script = $.el('script', { - textContent: code - }); - if (data) { - $.extend(script.dataset, data); - } - $.add(d.head || doc, script); - return $.rm(script); - }; - $.global = function(fn, data) { + var script; if (doc) { - return $.globalEval("(" + fn + ")();", data); + script = $.el('script', { + textContent: "(" + fn + ").call(document.currentScript.dataset);" + }); + if (data) { + $.extend(script.dataset, data); + } + $.add(d.head || doc, script); + $.rm(script); + return script.dataset; } else { - return fn(); + try { + fn.call(data); + } catch (error) {} + return data; } }; @@ -6627,23 +6628,17 @@ Fetcher = (function() { results1 = []; for (j = l = 0, len1 = ref.length; l < len1; j = ++l) { text2 = ref[j]; - results1.push({ - innerHTML: ((j % 2) ? "" + E(text2) + "" : E(text2)) - }); + results1.push({innerHTML: ((j % 2) ? "" + E(text2) + "" : E(text2))}); } return results1; })(); - text = { - innerHTML: ((greentext) ? "" + E.cat(text) + "" : E.cat(text)) - }; + text = {innerHTML: ((greentext) ? "" + E.cat(text) + "" : E.cat(text))}; results.push(text); } } return results; }).call(this); - comment = { - innerHTML: E.cat(comment) - }; + comment = {innerHTML: E.cat(comment)}; this.threadID = +data.thread_num; o = { ID: this.postID, @@ -6734,71 +6729,29 @@ Fetcher = (function() { }; Fetcher.prototype.archiveTags = { - '\n': { - innerHTML: "
" - }, - '[b]': { - innerHTML: "" - }, - '[/b]': { - innerHTML: "" - }, - '[spoiler]': { - innerHTML: "" - }, - '[/spoiler]': { - innerHTML: "" - }, - '[code]': { - innerHTML: "
"
-      },
-      '[/code]': {
-        innerHTML: "
" - }, - '[moot]': { - innerHTML: "
" - }, - '[/moot]': { - innerHTML: "
" - }, - '[banned]': { - innerHTML: "" - }, - '[/banned]': { - innerHTML: "" - }, + '\n': {innerHTML: "
"}, + '[b]': {innerHTML: ""}, + '[/b]': {innerHTML: ""}, + '[spoiler]': {innerHTML: ""}, + '[/spoiler]': {innerHTML: ""}, + '[code]': {innerHTML: "
"},
+      '[/code]': {innerHTML: "
"}, + '[moot]': {innerHTML: "
"}, + '[/moot]': {innerHTML: "
"}, + '[banned]': {innerHTML: ""}, + '[/banned]': {innerHTML: ""}, '[fortune]': function(text) { - return { - innerHTML: "" - }; + return {innerHTML: ""}; }, - '[/fortune]': { - innerHTML: "" - }, - '[i]': { - innerHTML: "" - }, - '[/i]': { - innerHTML: "" - }, - '[red]': { - innerHTML: "" - }, - '[/red]': { - innerHTML: "" - }, - '[green]': { - innerHTML: "" - }, - '[/green]': { - innerHTML: "" - }, - '[blue]': { - innerHTML: "" - }, - '[/blue]': { - innerHTML: "" - } + '[/fortune]': {innerHTML: ""}, + '[i]': {innerHTML: ""}, + '[/i]': {innerHTML: ""}, + '[red]': {innerHTML: ""}, + '[/red]': {innerHTML: ""}, + '[green]': {innerHTML: ""}, + '[/green]': {innerHTML: ""}, + '[blue]': {innerHTML: ""}, + '[/blue]': {innerHTML: ""} }; return Fetcher; @@ -6819,9 +6772,7 @@ Notice = (function() { this.onclose = onclose; this.close = bind(this.close, this); this.add = bind(this.add, this); - this.el = $.el('div', { - innerHTML: "
" - }); + this.el = $.el('div', {innerHTML: "
"}); this.el.style.opacity = 0; this.setType(type); $.on(this.el.firstElementChild, 'click', this.close); @@ -7115,8 +7066,8 @@ Post = (function() { } $.extend(file, { url: file.link.href, - isImage: /(jpe?g|png|gif|bmp)$/i.test(file.link.href), - isVideo: /(webm|mp4)$/i.test(file.link.href) + isImage: /\.(jpe?g|png|gif|bmp)$/i.test(file.link.href), + isVideo: /\.(webm|mp4)$/i.test(file.link.href) }); size = +file.size.match(/[\d.]+/)[0]; unit = ['B', 'KB', 'MB', 'GB'].indexOf(file.size.match(/\w+$/)[0]); @@ -8329,6 +8280,13 @@ SW = {}; }, isLinkified: function(link) { return ImageHost.test(link.hostname); + }, + testNativeExtension: function() { + return $.global(function() { + if (window.Parser.postMenuIcon) { + return this.enabled = 'true'; + } + }); } }; @@ -8508,9 +8466,7 @@ SW = {}; url = Build.threadURL(boardID, threadID); postLink = url + "#p" + ID; quoteLink = Build.sameThread(boardID, threadID) ? "javascript:quote('" + (+ID) + "');" : url + "#q" + ID; - postInfo = { - innerHTML: "
" + ((!o.isReply || boardID === "f" || subject) ? "" + E(subject || "") + " " : "") + "" + ((email) ? "" : "") + "" + E(name) + "" + ((tripcode) ? " " + E(tripcode) + "" : "") + ((o.extra.xa19s) ? " " + E(o.extra.xa19s) + "" : "") + ((pass) ? " " : "") + ((capcode) ? " ## " + E(capcode) + "" : "") + ((email) ? "" : "") + ((boardID === "f" && !o.isReply || capcodeDescription) ? "" : " ") + ((capcodeDescription) ? " \""" : "") + ((uniqueID && !capcode) ? " (ID: " + E(uniqueID) + ")" : "") + ((flagCode) ? " " : "") + ((flagCodeTroll) ? " \""" : "") + " " + E(dateText) + " No." + E(ID) + "" + ((o.extra.xa19l && o.isReply) ? " Like! ×" + E(o.extra.xa19l) + "" : "") + ((o.isSticky) ? " \"Sticky\"" : "") + ((o.isClosed && !o.isArchived) ? " \"Closed\"" : "") + ((o.isArchived) ? " \"Archived\"" : "") + ((!o.isReply && g.VIEW === "index") ? "   [Reply]" : "") + "
" - }; + postInfo = {innerHTML: "
" + ((!o.isReply || boardID === "f" || subject) ? "" + E(subject || "") + " " : "") + "" + ((email) ? "" : "") + "" + E(name) + "" + ((tripcode) ? " " + E(tripcode) + "" : "") + ((o.extra.xa19s) ? " " + E(o.extra.xa19s) + "" : "") + ((pass) ? " " : "") + ((capcode) ? " ## " + E(capcode) + "" : "") + ((email) ? "" : "") + ((boardID === "f" && !o.isReply || capcodeDescription) ? "" : " ") + ((capcodeDescription) ? " \""" : "") + ((uniqueID && !capcode) ? " (ID: " + E(uniqueID) + ")" : "") + ((flagCode) ? " " : "") + ((flagCodeTroll) ? " \""" : "") + " " + E(dateText) + " No." + E(ID) + "" + ((o.extra.xa19l && o.isReply) ? " Like! ×" + E(o.extra.xa19l) + "" : "") + ((o.isSticky) ? " \"Sticky\"" : "") + ((o.isClosed && !o.isArchived) ? " \"Closed\"" : "") + ((o.isArchived) ? " \"Archived\"" : "") + ((!o.isReply && g.VIEW === "index") ? "   [Reply]" : "") + "
"}; /* File Info */ if (file) { @@ -8519,15 +8475,11 @@ SW = {}; shortFilename = Build.shortFilename(file.name); fileThumb = file.isSpoiler ? Build.spoilerThumb(boardID) : file.thumbURL.replace(protocol, ''); } - fileBlock = { - innerHTML: ((file) ? "
" + ((boardID === "f") ? "
File: " + E(file.name) + "-(" + E(file.size) + ", " + E(file.dimensions) + ((file.tag) ? ", " + E(file.tag) : "") + ")
" : "
File: " + ((file.isSpoiler) ? "Spoiler Image" : E(shortFilename)) + " (" + E(file.size) + ", " + E(file.dimensions || "PDF") + ")
\""") + "
" : ((o.fileDeleted) ? "
\"File
" : "")) - }; + fileBlock = {innerHTML: ((file) ? "
" + ((boardID === "f") ? "
File: " + E(file.name) + "-(" + E(file.size) + ", " + E(file.dimensions) + ((file.tag) ? ", " + E(file.tag) : "") + ")
" : "
File: " + ((file.isSpoiler) ? "Spoiler Image" : E(shortFilename)) + " (" + E(file.size) + ", " + E(file.dimensions || "PDF") + ")
\""") + "
" : ((o.fileDeleted) ? "
\"File
" : ""))}; /* Whole Post */ postClass = o.isReply ? 'reply' : 'op'; - wholePost = { - innerHTML: ((o.isReply) ? "
>>
" : "") + "
" + ((o.isReply) ? (postInfo).innerHTML + (fileBlock).innerHTML : (fileBlock).innerHTML + (postInfo).innerHTML) + "
" + (commentHTML).innerHTML + "
" - }; + wholePost = {innerHTML: ((o.isReply) ? "
>>
" : "") + "
" + ((o.isReply) ? (postInfo).innerHTML + (fileBlock).innerHTML : (fileBlock).innerHTML + (postInfo).innerHTML) + "
" + (commentHTML).innerHTML + "
"}; container = $.el('div', { className: "postContainer " + postClass + "Container", id: "pc" + ID @@ -8618,9 +8570,7 @@ SW = {}; } postCount = data.replies + 1; fileCount = data.images + !!data.ext; - container = $.el('div', { - innerHTML: "
" + E(postCount) + " / " + E(fileCount) + " / " + E(pageCount) + "" + ((thread.isSticky) ? "" : "") + ((thread.isClosed) ? "" : "") + "
" - }); + container = $.el('div', {innerHTML: "
" + E(postCount) + " / " + E(fileCount) + " / " + E(pageCount) + "" + ((thread.isSticky) ? "" : "") + ((thread.isClosed) ? "" : "") + "
"}); $.before(thread.OP.nodes.info, slice.call(container.childNodes)); ref = $$('br', thread.OP.nodes.comment); for (i = 0, len = ref.length; i < len; i++) { @@ -8661,9 +8611,7 @@ SW = {}; link = Build.postURL(thread.board.ID, thread.ID, data.no); return $.el('div', { className: 'catalog-reply' - }, { - innerHTML: ": " + E(excerpt) + "..." - }); + }, {innerHTML: ": " + E(excerpt) + "..."}); } }; @@ -9077,7 +9025,6 @@ Filter = (function() { Filter = { filters: $.dict(), - results: $.dict(), 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' || ref === 'catalog') && Conf['Filter'])) { @@ -9472,7 +9419,7 @@ Filter = (function() { return ta.focus(); }); }, - quickFilterMD5: function() { + quickFilterMD5: function(e) { var files, filter, links, msg, notice, origin, post; post = Get.postFromNode(this); files = post.files.filter(function(f) { @@ -9491,15 +9438,19 @@ Filter = (function() { } else if (g.VIEW === 'index') { ThreadHiding.hide(origin.thread); } + if (e.type !== 'keydown') { + if (post.nodes.post.getBoundingClientRect().height) { + new Notice('info', 'MD5 filtered.', 2); + } + return; + } notice = Filter.quickFilterMD5.notice; if (notice) { notice.filters.push(filter); notice.posts.push(origin); return $('span', notice.el).textContent = notice.filters.length + " MD5s filtered."; } else { - msg = $.el('div', { - innerHTML: "MD5 filtered. [show] [undo]" - }); + msg = $.el('div', {innerHTML: "MD5 filtered. [show] [undo]"}); notice = Filter.quickFilterMD5.notice = new Notice('info', msg, void 0, function() { return delete Filter.quickFilterMD5.notice; }); @@ -10204,9 +10155,7 @@ ThreadHiding = (function() { className: type + "-thread-button", href: 'javascript:;' }); - $.extend(a, { - innerHTML: "" - }); + $.extend(a, {innerHTML: ""}); a.dataset.fullID = thread.fullID; $.on(a, 'click', ThreadHiding.toggle); return a; @@ -10524,13 +10473,22 @@ Header = (function() { Header = { init: function() { var barFixedToggler, barPositionToggler, box, cs, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler; + $.onExists(doc, 'body', (function(_this) { + return function() { + if (!Main.isThisPageLegit()) { + return; + } + $.add(_this.bar, [_this.noticesRoot, _this.toggle]); + $.prepend(d.body, _this.bar); + $.add(d.body, Header.hover); + return _this.setBarPosition(Conf['Bottom Header']); + }; + })(this)); this.menu = new UI.Menu('header'); menuButton = $.el('span', { className: 'menu-button' }); - $.extend(menuButton, { - innerHTML: "" - }); + $.extend(menuButton, {innerHTML: ""}); box = UI.checkbox; barFixedToggler = box('Fixed Header', 'Fixed Header'); headerToggler = box('Header auto-hide', 'Auto-hide header'); @@ -10606,16 +10564,6 @@ Header = (function() { $.on(window, 'load popstate', Header.hashScroll); $.on(d, 'CreateNotification', this.createNotification); this.setBoardList(); - $.onExists(doc, 'body', (function(_this) { - return function() { - if (!Main.isThisPageLegit()) { - return; - } - $.prepend(d.body, _this.bar); - $.add(d.body, Header.hover); - return _this.setBarPosition(Conf['Bottom Header']); - }; - })(this)); $.onExists(doc, g.SITE.selectors.boardList + " + *", Header.generateFullBoardList); Main.ready(function() { var a, absbot, footer, j, len, ref; @@ -10628,7 +10576,9 @@ Header = (function() { $('#navtopright', footer).id = 'navbotright'; $('#settingsWindowLink', footer).id = 'settingsWindowLinkBot'; $.before(absbot, footer); - $.globalEval('window.cloneTopNav = function() {};'); + $.global(function() { + return window.cloneTopNav = function() {}; + }); } if ((Header.bottomBoardList = $(g.SITE.selectors.boardListBottom))) { ref = $$('a', Header.bottomBoardList); @@ -10679,12 +10629,10 @@ Header = (function() { Header.boardList = boardList = $.el('span', { id: 'board-list' }); - $.extend(boardList, { - innerHTML: "" - }); + $.extend(boardList, {innerHTML: ""}); btn = $('.hide-board-list-button', boardList); $.on(btn, 'click', Header.toggleBoardList); - $.add(Header.bar, [Header.boardList, Header.shortcuts, Header.noticesRoot, Header.toggle]); + $.prepend(Header.bar, [Header.boardList, Header.shortcuts]); Header.setCustomNav(Conf['Custom Board Navigation']); Header.generateBoardList(Conf['boardnav']); $.sync('Custom Board Navigation', Header.setCustomNav); @@ -10971,8 +10919,10 @@ Header = (function() { return Header.previousOffset = offsetY; }, setBarPosition: function(bottom) { - var args; - Header.barPositionToggler.checked = bottom; + var args, ref; + if ((ref = Header.barPositionToggler) != null) { + ref.checked = bottom; + } $.event('CloseMenu'); args = bottom ? ['bottom-header', 'top-header', 'after'] : ['top-header', 'bottom-header', 'add']; $.addClass(doc, args[0]); @@ -11156,9 +11106,7 @@ Header = (function() { case 'denied': return; } - el = $.el('span', { - innerHTML: "4chan X needs your permission to show desktop notifications. [FAQ]
or " - }); + el = $.el('span', {innerHTML: "4chan X needs your permission to show desktop notifications. [FAQ]
or "}); ref = $$('button', el), authorize = ref[0], disable = ref[1]; $.on(authorize, 'click', function() { return Notification.requestPermission(function(status) { @@ -11281,9 +11229,7 @@ Index = (function() { this.navLinks = $.el('div', { className: 'navLinks json-index' }); - $.extend(this.navLinks, { - innerHTML: "Index Catalog Archive Bottom ×" - }); + $.extend(this.navLinks, {innerHTML: "Index Catalog Archive Bottom ×"}); $('.cataloglink a', this.navLinks).href = CatalogLinks.catalog(); if (!BoardConfig.isArchived(g.BOARD.ID)) { $('.archlistlink', this.navLinks).hidden = true; @@ -11331,9 +11277,7 @@ Index = (function() { this.pagelist = $.el('div', { className: 'pagelist json-index' }); - $.extend(this.pagelist, { - innerHTML: "
Catalog
" - }); + $.extend(this.pagelist, {innerHTML: "
Catalog
"}); $('.cataloglink a', this.pagelist).href = CatalogLinks.catalog(); $.on(this.pagelist, 'click', this.cb.pageNav); this.update(true); @@ -11421,9 +11365,7 @@ Index = (function() { el: $.el('a', { href: 'javascript:;', className: 'has-shortcut-text' - }, { - innerHTML: "Shift+click" - }), + }, {innerHTML: "Shift+click"}), order: 20, open: function(arg) { var thread; @@ -12084,7 +12026,7 @@ Index = (function() { return PostHiding.isHidden(g.BOARD.ID, threadID, replyData.no) || Filter.isHidden(g.SITE.Build.parseJSON(replyData, g.BOARD)); }, buildThreads: function(threadIDs, isCatalog, withReplies) { - var ID, OP, err, errors, isStale, k, lastPost, len1, newPosts, newThreads, obj, t, thread, threadData, threads; + var ID, OP, err, errors, isStale, k, lastPost, len1, newPosts, newThreads, obj, opRoot, t, thread, threadData, threads; threads = []; newThreads = []; newPosts = []; @@ -12119,7 +12061,8 @@ Index = (function() { thread.setPage(Math.floor(Index.threadPosition[ID] / Index.threadsNumPerPage) + 1); } else { obj = Index.parsedThreads[ID]; - OP = new Post(g.SITE.Build.post(obj), thread, g.BOARD); + opRoot = g.SITE.Build.post(obj); + OP = new Post(opRoot, thread, g.BOARD); OP.filterResults = obj.filterResults; newPosts.push(OP); } @@ -12133,7 +12076,8 @@ Index = (function() { } errors.push({ message: "Parsing of Thread No." + thread + " failed. Thread will be skipped.", - error: err + error: err, + html: opRoot != null ? opRoot.outerHTML : void 0 }); } } @@ -12185,7 +12129,8 @@ Index = (function() { } errors.push({ message: "Parsing of Post No." + data.no + " failed. Post will be skipped.", - error: err + error: err, + html: node != null ? node.outerHTML : void 0 }); } } @@ -12570,7 +12515,7 @@ Settings = (function() { $.on(d, 'OpenSettings', function(e) { return Settings.open(e.detail); }); - if (Conf['Disable Native Extension']) { + if (g.SITE.software === 'yotsuba' && Conf['Disable Native Extension']) { if ($.hasStorage) { return $.global(function() { var settings; @@ -12608,9 +12553,7 @@ Settings = (function() { $.event('CloseMenu'); Settings.dialog = dialog = $.el('div', { id: 'overlay' - }, { - innerHTML: "
" - }); + }, {innerHTML: "
"}); $.on($('.export', dialog), 'click', Settings["export"]); $.on($('.import', dialog), 'click', Settings["import"]); $.on($('.reset', dialog), 'click', Settings.reset); @@ -12699,9 +12642,7 @@ Settings = (function() { boardID: 'qa', threadID: 362590 }); - return cb($.el('li', { - innerHTML: "To protect yourself from malicious ads, you should block ads on 4chan." - })); + return cb($.el('li', {innerHTML: "To protect yourself from malicious ads, you should block ads on 4chan."})); }); }); } @@ -12710,9 +12651,7 @@ Settings = (function() { var addCheckboxes, addWarning, button, div, fs, inputs, items, key, keyFS, obj, ref, ref1, warning, warnings; warnings = $.el('fieldset', { hidden: true - }, { - innerHTML: "Warnings" - }); + }, {innerHTML: "Warnings"}); addWarning = function(item) { $.add($('ul', warnings), item); return warnings.hidden = false; @@ -12735,9 +12674,7 @@ Settings = (function() { continue; } description = arr[1]; - div = $.el('div', { - innerHTML: ": " + E(description) + "" - }); + div = $.el('div', {innerHTML: ": " + E(description) + ""}); div.dataset.name = key; input = $('input', div); $.on(input, 'change', $.cb.checked); @@ -12763,14 +12700,10 @@ Settings = (function() { ref1 = Config.main; for (keyFS in ref1) { obj = ref1[keyFS]; - fs = $.el('fieldset', { - innerHTML: "" + E(keyFS) + "" - }); + fs = $.el('fieldset', {innerHTML: "" + E(keyFS) + ""}); addCheckboxes(fs, obj); if (keyFS === 'Posting and Captchas') { - $.add(fs, $.el('p', { - innerHTML: "For more info on captcha options and issues, see the captcha FAQ." - })); + $.add(fs, $.el('p', {innerHTML: "For more info on captcha options and issues, see the captcha FAQ."})); } $.add(section, fs); } @@ -12789,9 +12722,7 @@ Settings = (function() { inputs[key].parentNode.parentNode.dataset.checked = val; } }); - div = $.el('div', { - innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." - }); + div = $.el('div', {innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply."}); button = $('button', div); $.get({ hiddenThreads: $.dict(), @@ -12935,17 +12866,20 @@ Settings = (function() { 'Disable 4chan\'s extension': 'Disable Native Extension', 'Comment Auto-Expansion': '', 'Remove Slug': '', + 'Always HTTPS': 'Redirect to HTTPS', 'Check for Updates': '', 'Recursive Filtering': 'Recursive Hiding', 'Reply Hiding': 'Reply Hiding Buttons', 'Thread Hiding': 'Thread Hiding Buttons', 'Show Stubs': 'Stubs', 'Image Auto-Gif': 'Replace GIF', + 'Expand All WebM': 'Expand videos', 'Reveal Spoilers': 'Reveal Spoiler Thumbnails', 'Expand From Current': 'Expand from here', 'Current Page': 'Page Count in Stats', 'Current Page Position': '', 'Alternative captcha': 'Use Recaptcha v1', + 'Alt index captcha': 'Use Recaptcha v1 on Index', 'Auto Submit': 'Post on Captcha Completion', 'Open Reply in New Tab': 'Open Post in New Tab', 'Remember QR size': 'Remember QR Size', @@ -12967,6 +12901,7 @@ Settings = (function() { 'spoiler': 'Spoiler tags', 'sageru': 'Toggle sage', 'code': 'Code tags', + 'sjis': 'SJIS tags', 'submit': 'Submit QR', 'watch': 'Watch', 'update': 'Update', @@ -12987,6 +12922,10 @@ Settings = (function() { 'Scrolling': 'Auto Scroll', 'Verbose': '' }); + if ('Always CDN' in data.Conf) { + data.Conf['fourchanImageHost'] = data.Conf['Always CDN'] ? 'i.4cdn.org' : ''; + delete data.Conf['Always CDN']; + } data.Conf.sauces = data.Conf.sauces.replace(/\$\d/g, function(c) { switch (c) { case '$1': @@ -13013,15 +12952,17 @@ Settings = (function() { } } if (data.WatchedThreads) { - data.Conf['watchedThreads'] = { - boards: $.dict() - }; + data.Conf['watchedThreads'] = $.dict.clone({ + '4chan.org': { + boards: {} + } + }); ref1 = data.WatchedThreads; for (boardID in ref1) { threads = ref1[boardID]; for (threadID in threads) { threadData = threads[threadID]; - ((base = data.Conf['watchedThreads'].boards)[boardID] || (base[boardID] = $.dict()))[threadID] = { + ((base = data.Conf['watchedThreads']['4chan.org'].boards)[boardID] || (base[boardID] = $.dict()))[threadID] = { excerpt: threadData.textContent }; } @@ -13361,9 +13302,7 @@ Settings = (function() { }, filter: function(section) { var select; - $.extend(section, { - innerHTML: "
" - }); + $.extend(section, {innerHTML: "
"}); select = $('select', section); $.on(select, 'change', Settings.selectFilter); return Settings.selectFilter.call(select); @@ -13391,20 +13330,14 @@ Settings = (function() { filterTypes = Object.keys(Config.filter).filter(function(x) { return x !== 'general'; }).map(function(x, i) { - return { - innerHTML: ((i) ? "," : "") + "" + E(x) - }; - }); - $.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.

" + return {innerHTML: ((i) ? "," : "") + "" + E(x)}; }); + $.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.

"}); return $('.warning', div).hidden = Conf['Filter']; }, sauce: function(section) { var ta; - $.extend(section, { - innerHTML: "
Sauce is disabled.
These parameters will be replaced by their corresponding values in the URL and displayed text:
  • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
  • %URL: Full image URL.
  • %TURL: Thumbnail URL.
  • %name: Original file name.
  • %board: Current board.
  • %MD5: MD5 hash in base64.
  • %sMD5: MD5 hash in base64 using - and _.
  • %hMD5: MD5 hash in hexadecimal.
  • %$0: Matched regular expression within the filename.
  • %$1, %$2, %$3, ... : Subexpressions within the matched regular expression.
  • %%, %semi: Literal % and ;.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards/sites by appending ;boards:[board1],[board2]. See the Filter guide for details.
You can specify the applicable file types by appending ;types:[extension1],[extension2].
You can specify a regular expression the filename must match by appending ;regexp:[regular expression].
" - }); + $.extend(section, {innerHTML: "
Sauce is disabled.
These parameters will be replaced by their corresponding values in the URL and displayed text:
  • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
  • %URL: Full image URL.
  • %TURL: Thumbnail URL.
  • %name: Original file name.
  • %board: Current board.
  • %MD5: MD5 hash in base64.
  • %sMD5: MD5 hash in base64 using - and _.
  • %hMD5: MD5 hash in hexadecimal.
  • %$0: Matched regular expression within the filename.
  • %$1, %$2, %$3, ... : Subexpressions within the matched regular expression.
  • %%, %semi: Literal % and ;.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards/sites by appending ;boards:[board1],[board2]. See the Filter guide for details.
You can specify the applicable file types by appending ;types:[extension1],[extension2].
You can specify a regular expression the filename must match by appending ;regexp:[regular expression].
"}); $('.warning', section).hidden = Conf['Sauce']; ta = $('textarea', section); $.get('sauces', Conf['sauces'], function(item) { @@ -13415,9 +13348,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"
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.

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, options (or equivalently email), subject and password.
  • Wrap values of items with quotes, like this: options:"sage".
  • Force values as defaults with the always keyword, for example: options:"sage";always.
  • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
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:
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"
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.

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, options (or equivalently email), subject and password.
  • Wrap values of items with quotes, like this: options:"sage".
  • Force values as defaults with the always keyword, for example: options:"sage";always.
  • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
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]; @@ -13607,9 +13538,7 @@ Settings = (function() { textContent: archive[1] })); } - $.extend(td, { - innerHTML: "" - }); + $.extend(td, {innerHTML: ""}); select = td.firstElementChild; if (!(select.disabled = length === 1)) { select.setAttribute('data-boardid', boardID); @@ -13731,9 +13660,7 @@ Settings = (function() { }, keybinds: function(section) { var arr, input, inputs, items, key, ref, tbody, tr; - $.extend(section, { - innerHTML: "
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
" - }); + $.extend(section, {innerHTML: "
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
"}); $('.warning', section).hidden = Conf['Keybinds']; tbody = $('tbody', section); items = $.dict(); @@ -13741,9 +13668,7 @@ Settings = (function() { ref = Config.hotkeys; for (key in ref) { arr = ref[key]; - tr = $.el('tr', { - innerHTML: "" + E(arr[1]) + "" - }); + tr = $.el('tr', {innerHTML: "" + E(arr[1]) + ""}); input = $('input', tr); input.name = key; input.spellcheck = false; @@ -14424,9 +14349,7 @@ Gallery = (function() { nodes.el = dialog = $.el('div', { id: 'a-gallery' }); - $.extend(dialog, { - innerHTML: "
× /
" - }); + $.extend(dialog, {innerHTML: "
× /
"}); ref = { buttons: '.gal-buttons', frame: '.gal-image', @@ -14875,9 +14798,7 @@ Gallery = (function() { } return results; })(); - delayLabel = $.el('label', { - innerHTML: "Slide Delay: " - }); + delayLabel = $.el('label', {innerHTML: "Slide Delay: "}); delayInput = delayLabel.firstElementChild; delayInput.value = Gallery.delay; $.on(delayInput, 'change', Gallery.cb.setDelay); @@ -14991,7 +14912,7 @@ ImageCommon = (function() { onloadend: function() { var i, len, postObj, ref, ref1; if (this.status === 404) { - post.kill(!post.isClone); + post.kill(!post.isClone, fileObj.index); } if (this.status !== 200) { return redirect(); @@ -15083,9 +15004,7 @@ ImageExpand = (function() { this.videoControls = $.el('span', { className: 'video-controls' }); - $.extend(this.videoControls, { - innerHTML: " contract" - }); + $.extend(this.videoControls, {innerHTML: " contract"}); return Callbacks.Post.push({ name: 'Image Expansion', cb: this.node @@ -15693,9 +15612,7 @@ ImageLoader = (function() { if (!(Conf['Image Prefetching'] && ((ref1 = g.VIEW) === 'index' || ref1 === 'thread'))) { return; } - prefetch = $.el('label', { - innerHTML: " Prefetch Images" - }); + prefetch = $.el('label', {innerHTML: " Prefetch Images"}); this.el = prefetch.firstElementChild; $.on(this.el, 'change', this.toggle); return Header.menu.addEntry({ @@ -15741,12 +15658,19 @@ ImageLoader = (function() { return file.videoThumb = true; }, prefetch: function(post, file) { - var clone, el, i, isImage, isVideo, len, match, ref, replace, thumb, type, url; + var clone, el, i, isImage, isVideo, len, ref, ref1, replace, thumb, type, url; isImage = file.isImage, isVideo = file.isVideo, thumb = file.thumb, url = file.url; if (file.isPrefetched || !(isImage || isVideo) || post.isHidden || post.thread.isHidden) { return; } - type = (match = url.match(/\.([^.]+)$/)[1].toUpperCase()) === 'JPEG' ? 'JPG' : match; + if (isVideo) { + type = 'WEBM'; + } else { + type = (ref = url.match(/\.([^.]+)$/)) != null ? ref[1].toUpperCase() : void 0; + if (type === 'JPEG') { + type = 'JPG'; + } + } replace = Conf["Replace " + type] && !/spoiler/.test(thumb.src || thumb.dataset.src); if (!(replace || Conf['prefetch'])) { return; @@ -15761,9 +15685,9 @@ ImageLoader = (function() { } file.isPrefetched = true; if (file.videoThumb) { - ref = post.clones; - for (i = 0, len = ref.length; i < len; i++) { - clone = ref[i]; + ref1 = post.clones; + for (i = 0, len = ref1.length; i < len; i++) { + clone = ref1[i]; clone.file.thumb.preload = 'auto'; } thumb.preload = 'auto'; @@ -15777,10 +15701,10 @@ ImageLoader = (function() { el = $.el(isImage ? 'img' : 'video'); if (replace && isImage) { $.on(el, 'load', function() { - var j, len1, ref1; - ref1 = post.clones; - for (j = 0, len1 = ref1.length; j < len1; j++) { - clone = ref1[j]; + var j, len1, ref2; + ref2 = post.clones; + for (j = 0, len1 = ref2.length; j < len1; j++) { + clone = ref2[j]; clone.file.thumb.src = url; } return thumb.src = url; @@ -15860,9 +15784,7 @@ Metadata = (function() { className: 'webm-title' }); el.dataset.index = i; - $.extend(el, { - innerHTML: "" - }); + $.extend(el, {innerHTML: ""}); $.add(file.text, [$.tn(' '), el]); } if (el.children.length === 1) { @@ -16249,9 +16171,7 @@ Volume = (function() { volumeEntry = $.el('label', { title: 'Default volume for videos.' }); - $.extend(volumeEntry, { - innerHTML: " Volume" - }); + $.extend(volumeEntry, {innerHTML: " Volume"}); this.inputs = { unmute: unmuteEntry.firstElementChild, volume: volumeEntry.firstElementChild @@ -16361,20 +16281,18 @@ Embedding = (function() { this.types[type.key] = type; } if (Conf['Embedding'] && g.VIEW !== 'archive') { - this.dialog = UI.dialog('embedding', { - innerHTML: "
×
" - }); + this.dialog = UI.dialog('embedding', {innerHTML: "
×
"}); this.media = $('#media-embed', this.dialog); $.one(d, '4chanXInitFinished', this.ready); $.on(d, 'IndexRefreshInternal', function() { return g.posts.forEach(function(post) { - var embed, k, l, len1, len2, ref2, ref3; + var embed, l, len1, len2, n, ref2, ref3; ref2 = [post].concat(slice.call(post.clones)); - for (k = 0, len1 = ref2.length; k < len1; k++) { - post = ref2[k]; + for (l = 0, len1 = ref2.length; l < len1; l++) { + post = ref2[l]; ref3 = post.nodes.embedlinks; - for (l = 0, len2 = ref3.length; l < len2; l++) { - embed = ref3[l]; + for (n = 0, len2 = ref3.length; n < len2; n++) { + embed = ref3[n]; Embedding.cb.catalogRemove.call(embed); } } @@ -16382,7 +16300,7 @@ Embedding = (function() { }); } if (Conf['Link Title']) { - return $.on(d, '4chanXInitFinished PostsInserted', function() { + $.on(d, '4chanXInitFinished PostsInserted', function() { var key, ref2, ref3, service; ref2 = Embedding.types; for (key in ref2) { @@ -16392,6 +16310,8 @@ Embedding = (function() { } } }); + this.cache.init(Conf['cachedTitles']); + return $.sync('cachedTitles', this.cache.sync); } }, events: function(post) { @@ -16464,9 +16384,7 @@ Embedding = (function() { embed = $.el('a', { className: 'embedder', href: 'javascript:;' - }, { - innerHTML: "(unembed)" - }); + }, {innerHTML: "(unembed)"}); ref = { key: key, uid: uid, @@ -16521,13 +16439,15 @@ Embedding = (function() { return style.pointerEvents = 'none'; }, title: function(data) { - var key, link, options, post, service, uid; + var key, link, options, post, service, text, uid; key = data.key, uid = data.uid, options = data.options, link = data.link, post = data.post; if (!(service = Embedding.types[key].title)) { return; } $.addClass(link, key.toLowerCase()); - if (service.batchSize) { + if ((text = Embedding.cache.get(data))) { + return Embedding.cb.titleText(text, data); + } else if (service.batchSize) { (service.queue || (service.queue = [])).push(data); if (service.queue.length >= service.batchSize) { return Embedding.flushTitles(service); @@ -16562,6 +16482,70 @@ Embedding = (function() { return results; })()), cb); }, + cache: (function() { + var get, init, newEntries, save, set, sync, titles; + titles = $.dict(); + newEntries = []; + init = function(data) { + var j, key, len, ref, text, uid; + try { + for (j = 0, len = data.length; j < len; j++) { + ref = data[j], key = ref.key, uid = ref.uid, text = ref.text; + titles[key + "." + uid] = text; + } + } catch (error) {} + }; + sync = function(data) { + var j, k, key, len, ref, text, uid; + try { + for (j = 0, len = data.length; j < len; j++) { + ref = data[j], key = ref.key, uid = ref.uid, text = ref.text; + k = key + "." + uid; + if (k in titles) { + break; + } + titles[k] = text; + } + } catch (error) {} + }; + get = function(arg) { + var key, uid; + key = arg.key, uid = arg.uid; + return titles[key + "." + uid]; + }; + set = function(arg, text) { + var key, uid; + key = arg.key, uid = arg.uid; + titles[key + "." + uid] = text; + newEntries.push({ + key: key, + uid: uid, + text: text + }); + return save(); + }; + save = $.debounce(2 * $.SECOND, function() { + return $.get('cachedTitles', Conf['cachedTitles'], function(arg) { + var cachedTitles; + cachedTitles = arg.cachedTitles; + sync(cachedTitles); + try { + cachedTitles = newEntries.concat(cachedTitles).slice(-100); + } catch (error) { + cachedTitles = newEntries; + } + newEntries = []; + return $.set('cachedTitles', cachedTitles); + }); + }); + return { + init: init, + sync: sync, + get: get, + set: set, + save: save + }; + })(), preview: function(data) { var key, link, service, uid; key = data.key, uid = data.uid, link = data.link; @@ -16627,34 +16611,42 @@ Embedding = (function() { } }, title: function(req, data) { - var base1, j, k, key, len, len1, link, link2, options, post, post2, ref, ref1, service, status, text, uid; + var key, service, status, text, uid; if (!req.status) { return; } - key = data.key, uid = data.uid, options = data.options, link = data.link, post = data.post; + key = data.key, uid = data.uid; status = req.status; service = Embedding.types[key].title; - text = "[" + key + "] " + ((function() { - switch (status) { - case 200: - case 304: - return service.text(req.response, uid); - case 404: - return "Not Found"; - case 403: - return "Forbidden or Private"; - default: - return status + "'d"; - } - })()); + switch (status) { + case 200: + case 304: + text = service.text(req.response, uid); + Embedding.cache.set(data, text); + break; + case 404: + text = "Not Found"; + break; + case 403: + text = "Forbidden or Private"; + break; + default: + text = status + "'d"; + } + return Embedding.cb.titleText(text, data); + }, + titleText: function(text, data) { + var base1, j, key, l, len, len1, link, link2, post, post2, ref, ref1; + key = data.key, link = data.link, post = data.post; + text = "[" + key + "] " + text; link.dataset.original = link.textContent; link.textContent = text; ref = post.clones; for (j = 0, len = ref.length; j < len; j++) { post2 = ref[j]; ref1 = $$('a.linkify', post2.nodes.comment); - for (k = 0, len1 = ref1.length; k < len1; k++) { - link2 = ref1[k]; + for (l = 0, len1 = ref1.length; l < len1; l++) { + link2 = ref1[l]; if (!(link2.href === link.href)) { continue; } @@ -16683,9 +16675,7 @@ Embedding = (function() { regExp: /^[^?#]+\.(?:gif|png|jpg|jpeg|bmp)(?::\w+)?(?:[?#]|$)/i, style: '', el: function(a) { - return $.el('div', { - innerHTML: "" - }); + return $.el('div', {innerHTML: ""}); } }, { key: 'video', @@ -16845,10 +16835,10 @@ Embedding = (function() { } }, { key: 'Loopvid', - regExp: /^\w+:\/\/(?:www\.)?loopvid.appspot.com\/#?((?:pf|kd|lv|gd|gh|db|dx|nn|cp|wu|ig|ky|mf|m2|pc|1c|pi|ni|wl|ko|gc)\/[\w\-\/]+(?:,[\w\-\/]+)*|fc\/\w+\/\d+|https?:\/\/.+)/, + regExp: /^\w+:\/\/(?:www\.)?loopvid.appspot.com\/#?((?:pf|kd|lv|gd|gh|db|dx|nn|cp|wu|ig|ky|mf|m2|pc|1c|pi|ni|wl|ko|mm|ic|gc)\/[\w\-\/]+(?:,[\w\-\/]+)*|fc\/\w+\/\d+|https?:\/\/.+)/, style: 'max-width: 80vw; max-height: 80vh;', el: function(a) { - var _, base, el, host, j, k, l, len, len1, len2, name, names, ref, ref1, type, types, url, urls; + var _, base, el, host, j, l, len, len1, len2, n, name, names, ref, ref1, type, types, url, urls; el = $.el('video', { controls: true, preload: 'auto', @@ -16876,8 +16866,8 @@ Embedding = (function() { ref1 = names.split(','); for (j = 0, len = ref1.length; j < len; j++) { name = ref1[j]; - for (k = 0, len1 = types.length; k < len1; k++) { - type = types[k]; + for (l = 0, len1 = types.length; l < len1; l++) { + type = types[l]; base = "" + name + type; urls = (function() { switch (host) { @@ -16910,25 +16900,29 @@ Embedding = (function() { case 'm2': return ["https://kastden.org/_loopvid_media/m2/" + base]; case 'pc': - return ["http://a.pomf.cat/" + base]; + return ["https://kastden.org/_loopvid_media/pc/" + base, "https://web.archive.org/web/2/http://a.pomf.cat/" + base]; case '1c': return ["http://b.1339.cf/" + base]; case 'pi': - return ["https://u.pomf.is/" + base]; + return ["https://kastden.org/_loopvid_media/pi/" + base, "https://web.archive.org/web/2/https://u.pomf.is/" + base]; case 'ni': - return ["https://u.nya.is/" + base]; + return ["https://kastden.org/_loopvid_media/ni/" + base, "https://web.archive.org/web/2/https://u.nya.is/" + base]; case 'wl': return ["http://webm.land/media/" + base]; case 'ko': return ["https://kordy.kastden.org/loopvid/" + base]; + case 'mm': + return ["https://kastden.org/_loopvid_media/mm/" + base, "https://web.archive.org/web/2/https://my.mixtape.moe/" + base]; + case 'ic': + return ["https://media.8ch.net/file_store/" + base]; case 'fc': return ["//" + (ImageHost.host()) + "/" + base + ".webm"]; case 'gc': return ["https://" + type + ".gfycat.com/" + name + ".webm"]; } })(); - for (l = 0, len2 = urls.length; l < len2; l++) { - url = urls[l]; + for (n = 0, len2 = urls.length; n < len2; n++) { + url = urls[n]; $.add(el, $.el('source', { src: url })); @@ -17567,9 +17561,7 @@ DeleteLink = (function() { } link.textContent = DeleteLink.linkText(fileOnly); if (resDoc.title === '4chan - Banned') { - el = $.el('span', { - innerHTML: "You can't delete posts because you are banned." - }); + el = $.el('span', {innerHTML: "You can't delete posts because you are banned."}); return new Notice('warning', el, 20); } else if (msg = resDoc.getElementById('errmsg')) { new Notice('warning', msg.textContent, 20); @@ -17681,9 +17673,7 @@ Menu = (function() { className: 'menu-button', href: 'javascript:;' }); - $.extend(this.button, { - innerHTML: "" - }); + $.extend(this.button, {innerHTML: ""}); this.menu = new UI.Menu('post'); Callbacks.Post.push({ name: 'Menu', @@ -18542,14 +18532,10 @@ FileInfo = (function() { var a, i, j, len, len1, output, ref, ref1; output = []; formatString.replace(/%(.)|[^%]+/g, function(s, c) { - output.push($.hasOwn(FileInfo.formatters, c) ? FileInfo.formatters[c].call(post) : { - innerHTML: E(s) - }); + output.push($.hasOwn(FileInfo.formatters, c) ? FileInfo.formatters[c].call(post) : {innerHTML: E(s)}); return ''; }); - $.extend(outputNode, { - innerHTML: E.cat(output) - }); + $.extend(outputNode, {innerHTML: E.cat(output)}); ref = $$('.download-button', outputNode); for (i = 0, len = ref.length; i < len; i++) { a = ref[i]; @@ -18563,93 +18549,59 @@ FileInfo = (function() { }, formatters: { t: function() { - return { - innerHTML: E(this.file.url.match(/[^/]*$/)[0]) - }; + return {innerHTML: E(this.file.url.match(/[^/]*$/)[0])}; }, T: function() { - return { - innerHTML: "" + (FileInfo.formatters.t.call(this)).innerHTML + "" - }; + return {innerHTML: "" + (FileInfo.formatters.t.call(this)).innerHTML + ""}; }, l: function() { - return { - innerHTML: "" + (FileInfo.formatters.n.call(this)).innerHTML + "" - }; + return {innerHTML: "" + (FileInfo.formatters.n.call(this)).innerHTML + ""}; }, L: function() { - return { - innerHTML: "" + (FileInfo.formatters.N.call(this)).innerHTML + "" - }; + return {innerHTML: "" + (FileInfo.formatters.N.call(this)).innerHTML + ""}; }, n: function() { var fullname, shortname; fullname = this.file.name; shortname = g.SITE.Build.shortFilename(this.file.name, this.isReply); if (fullname === shortname) { - return { - innerHTML: E(fullname) - }; + return {innerHTML: E(fullname)}; } else { - return { - innerHTML: "" + E(shortname) + "" + E(fullname) + "" - }; + return {innerHTML: "" + E(shortname) + "" + E(fullname) + ""}; } }, N: function() { - return { - innerHTML: E(this.file.name) - }; + return {innerHTML: E(this.file.name)}; }, d: function() { - return { - innerHTML: "" - }; + return {innerHTML: ""}; }, f: function() { - return { - innerHTML: "" - }; + return {innerHTML: ""}; }, p: function() { - return { - innerHTML: ((this.file.isSpoiler) ? "Spoiler, " : "") - }; + return {innerHTML: ((this.file.isSpoiler) ? "Spoiler, " : "")}; }, s: function() { - return { - innerHTML: E(this.file.size) - }; + return {innerHTML: E(this.file.size)}; }, B: function() { - return { - innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes" - }; + return {innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes"}; }, K: function() { - return { - innerHTML: E(Math.round(this.file.sizeInBytes/1024)) + " KB" - }; + return {innerHTML: E(Math.round(this.file.sizeInBytes/1024)) + " KB"}; }, M: function() { - return { - innerHTML: E(Math.round(this.file.sizeInBytes/1048576*100)/100) + " MB" - }; + return {innerHTML: E(Math.round(this.file.sizeInBytes/1048576*100)/100) + " MB"}; }, r: function() { - return { - innerHTML: E(this.file.dimensions || "PDF") - }; + return {innerHTML: E(this.file.dimensions || "PDF")}; }, g: function() { - return { - innerHTML: ((this.file.tag) ? ", " + E(this.file.tag) : "") - }; + return {innerHTML: ((this.file.tag) ? ", " + E(this.file.tag) : "")}; }, '%': function() { - return { - innerHTML: "%" - }; + return {innerHTML: "%"}; } } }; @@ -18718,7 +18670,17 @@ Fourchan = (function() { return $.addClass(pre, 'prettyprinted'); } }); - $.globalEval('window.addEventListener(\'prettyprint\', function(e) {\n window.dispatchEvent(new CustomEvent(\'prettyprint:cb\', {\n detail: {\n ID: e.detail.ID,\n i: e.detail.i,\n html: prettyPrintOne(e.detail.html)\n }\n }));\n}, false);'); + $.global(function() { + return window.addEventListener('prettyprint', function(e) { + return window.dispatchEvent(new CustomEvent('prettyprint:cb', { + detail: { + ID: e.detail.ID, + i: e.detail.i, + html: window.prettyPrintOne(e.detail.html) + } + })); + }, false); + }); Callbacks.Post.push({ name: 'Parse [code] tags', cb: Fourchan.code @@ -19317,7 +19279,7 @@ Keybinds = (function() { } post = Keybinds.post(threadRoot); Keybinds.hl(+1, threadRoot); - Filter.quickFilterMD5.call(post); + Filter.quickFilterMD5.call(post, e); break; case Conf['Previous Post Quoting You']: if (!(threadRoot && QuoteYou.db)) { @@ -19531,34 +19493,24 @@ ModContact = (function() { } }, template: function(capcode) { - return { - innerHTML: "feedback" + (ModContact.specific[capcode]()).innerHTML - }; + return {innerHTML: "feedback" + (ModContact.specific[capcode]()).innerHTML}; }, specific: { Mod: function() { - return { - innerHTML: " IRC" - }; + return {innerHTML: " IRC"}; }, Manager: function() { return ModContact.specific.Mod(); }, Developer: function() { - return { - innerHTML: " github" - }; + return {innerHTML: " github"}; }, Admin: function() { - return { - innerHTML: " twitter" - }; + return {innerHTML: " twitter"}; } }, moveNote: { - qa: { - innerHTML: "Moving a thread to /qa/ does not imply mods will read it. If you wish to contact mods, use feedback (https://www.4chan.org/feedback) or IRC (https://www.4chan-x.net/4chan-irc.html)." - } + qa: {innerHTML: "Moving a thread to /qa/ does not imply mods will read it. If you wish to contact mods, use feedback (https://www.4chan.org/feedback) or IRC (https://www.4chan-x.net/4chan-irc.html)."} } }; @@ -19889,9 +19841,7 @@ PostJumper = (function() { span = $.el('span', { className: 'postJumper' }); - $.extend(span, { - innerHTML: "" + E(charPrev) + "" + E(charNext) + "" - }); + $.extend(span, {innerHTML: "" + E(charPrev) + "" + E(charNext) + ""}); return span; }, scroll: function(fromJumper, toJumper) { @@ -20121,9 +20071,7 @@ Report = (function() { fieldset = $.el('fieldset', { id: 'archive-report', hidden: true - }, { - innerHTML: "" - }); + }, {innerHTML: ""}); enabled = $('#archive-report-enabled', fieldset); reason = $('#archive-report-reason', fieldset); submit = $('#archive-report-submit', fieldset); @@ -20590,9 +20538,7 @@ ReplyPruning = (function() { label = UI.checkbox('Prune Replies', 'Show Last', Conf['Prune All Threads']); el = $.el('span', { title: 'Maximum number of replies to show.' - }, { - innerHTML: " " - }); + }, {innerHTML: " "}); $.prepend(el, label); this.inputs = { enabled: label.firstElementChild, @@ -20736,9 +20682,7 @@ ThreadStats = (function() { if (Conf['Page Count in Stats']) { this[(typeof (base = g.SITE).isPrunedByAge === "function" ? base.isPrunedByAge(g.BOARD) : void 0) ? 'showPurgePos' : 'showPage'] = true; } - statsHTML = { - innerHTML: "? / ?" + ((Conf["IP Count in Stats"] && g.SITE.hasIPCount) ? " / ?" : "") + ((Conf["Page Count in Stats"]) ? " / ?" : "") - }; + statsHTML = {innerHTML: "? / ?" + ((Conf["IP Count in Stats"] && g.SITE.hasIPCount) ? " / ?" : "") + ((Conf["Page Count in Stats"]) ? " / ?" : "")}; statsTitle = 'Posts / Files'; if (Conf['IP Count in Stats'] && g.SITE.hasIPCount) { statsTitle += ' / IPs'; @@ -20754,9 +20698,7 @@ ThreadStats = (function() { $.extend(sc, statsHTML); Header.addShortcut('stats', sc, 200); } else { - this.dialog = sc = UI.dialog('thread-stats', { - innerHTML: "
" + (statsHTML).innerHTML + "
" - }); + this.dialog = sc = UI.dialog('thread-stats', {innerHTML: "
" + (statsHTML).innerHTML + "
"}); $.addClass(doc, 'float'); $.ready(function() { return $.add(d.body, sc); @@ -20925,14 +20867,10 @@ ThreadUpdater = (function() { this.dialog = sc = $.el('span', { id: 'updater' }); - $.extend(sc, { - innerHTML: "" - }); + $.extend(sc, {innerHTML: ""}); Header.addShortcut('updater', sc, 100); } else { - this.dialog = sc = UI.dialog('updater', { - innerHTML: "
" - }); + this.dialog = sc = UI.dialog('updater', {innerHTML: "
"}); $.addClass(doc, 'float'); $.ready(function() { return $.add(d.body, sc); @@ -20946,9 +20884,7 @@ ThreadUpdater = (function() { updateLink = $.el('span', { className: 'brackets-wrap updatelink' }); - $.extend(updateLink, { - innerHTML: "Update" - }); + $.extend(updateLink, {innerHTML: "Update"}); Main.ready(function() { var navLinksBot; if ((navLinksBot = $('.navLinksBot'))) { @@ -20974,9 +20910,7 @@ ThreadUpdater = (function() { el: el }); } - this.settings = $.el('span', { - innerHTML: "Interval" - }); + this.settings = $.el('span', {innerHTML: "Interval"}); $.on(this.settings, 'click', this.intervalShortcut); subEntries.push({ el: this.settings @@ -21359,9 +21293,7 @@ ThreadWatcher = (function() { }); this.db = new DataBoard('watchedThreads', this.refresh, true); this.dbLM = new DataBoard('watcherLastModified', null, true); - this.dialog = UI.dialog('thread-watcher', { - innerHTML: "
Thread Watcher ×
" - }); + this.dialog = UI.dialog('thread-watcher', {innerHTML: "
Thread Watcher ×
"}); this.status = $('#watcher-status', this.dialog); this.list = this.dialog.lastElementChild; this.refreshButton = $('.refresh', this.dialog); @@ -21399,9 +21331,7 @@ ThreadWatcher = (function() { el: $.el('a', { href: 'javascript:;', className: 'has-shortcut-text' - }, { - innerHTML: "Alt+click" - }), + }, {innerHTML: "Alt+click"}), order: 6, open: function(arg) { var thread; @@ -23486,9 +23416,7 @@ Captcha = {}; root = $.el('div', { className: 'captcha-root' }); - $.extend(root, { - innerHTML: "
" - }); + $.extend(root, {innerHTML: "
"}); counter = $('.captcha-counter > a', root); this.nodes = { root: root, @@ -23767,9 +23695,7 @@ PassLink = (function() { passLink = $.el('span', { className: 'brackets-wrap pass-link-container' }); - $.extend(passLink, { - innerHTML: "4chan Pass" - }); + $.extend(passLink, {innerHTML: "4chan Pass"}); $.on(passLink.firstElementChild, 'click', function() { return window.open("//sys." + (location.hostname.split('.')[1]) + ".org/auth", Date.now(), 'width=500,height=280,toolbar=0'); }); @@ -23940,9 +23866,7 @@ QR = (function() { link = $.el('h1', { className: "qr-link-container" }); - $.extend(link, { - innerHTML: "" + ((g.VIEW === "thread") ? "Reply to Thread" : "Start a Thread") + "" - }); + $.extend(link, {innerHTML: "" + ((g.VIEW === "thread") ? "Reply to Thread" : "Start a Thread") + ""}); QR.link = link.firstElementChild; $.on(link.firstChild, 'click', function() { QR.open(); @@ -23955,9 +23879,7 @@ QR = (function() { linkBot = $.el('div', { className: "brackets-wrap qr-link-container-bottom" }); - $.extend(linkBot, { - innerHTML: "Reply to Thread" - }); + $.extend(linkBot, {innerHTML: "Reply to Thread"}); $.on(linkBot.firstElementChild, 'click', function() { QR.open(); return QR.nodes.com.focus(); @@ -24156,9 +24078,7 @@ QR = (function() { } }, connectionError: function() { - return $.el('span', { - innerHTML: "Connection error while posting. [More info]" - }); + return $.el('span', {innerHTML: "Connection error while posting. [More info]"}); }, notifications: [], cleanNotifications: function() { @@ -24306,9 +24226,7 @@ QR = (function() { openError: function() { var div; div = $.el('div'); - $.extend(div, { - innerHTML: "Could not open file. [More info]" - }); + $.extend(div, {innerHTML: "Could not open file. [More info]"}); return QR.error(div); }, setFile: function(e) { @@ -24486,9 +24404,7 @@ QR = (function() { dialog: function() { var classList, config, dialog, event, i, items, name, node, nodes, save, setNode; QR.nodes = nodes = { - el: dialog = UI.dialog('qr', { - innerHTML: "
×
No selected file
" - }) + el: dialog = UI.dialog('qr', {innerHTML: "
×
No selected file
"}) }; setNode = function(name, query) { return nodes[name] = $(query, dialog); @@ -24544,6 +24460,7 @@ QR = (function() { QR.flagsInput(); $.on(nodes.autohide, 'change', QR.toggleHide); $.on(nodes.close, 'click', QR.close); + $.on(nodes.status, 'click', QR.submit); $.on(nodes.form, 'submit', QR.submit); $.on(nodes.sjisToggle, 'click', QR.toggleSJIS); $.on(nodes.texButton, 'mousedown', QR.texPreviewShow); @@ -24657,19 +24574,24 @@ QR = (function() { } }, submit: function(e) { - var captcha, cb, err, filetag, formData, options, post, ref, thread, threadID; + var captcha, cb, err, filetag, force, formData, options, post, ref, thread, threadID; if (e != null) { e.preventDefault(); } + force = e != null ? e.shiftKey : void 0; if (QR.req) { QR.abort(); return; } $.forceSync('cooldowns'); if (QR.cooldown.seconds) { - QR.cooldown.auto = !QR.cooldown.auto; - QR.status(); - return; + if (force) { + QR.cooldown.clear(); + } else { + QR.cooldown.auto = !QR.cooldown.auto; + QR.status(); + return; + } } post = QR.posts[0]; post.forceSave(); @@ -24703,7 +24625,7 @@ QR = (function() { } } QR.cleanNotifications(); - if (err) { + if (err && !force) { QR.cooldown.auto = false; QR.status(); QR.error(err); @@ -24789,7 +24711,7 @@ QR = (function() { return QR.status(); }, response: function() { - var URL, _, connErr, err, h1, isReply, lastPostToThread, m, open, post, postID, postsCount, ref, ref1, ref2, ref3, seconds, threadID; + var URL, _, connErr, err, h1, isReply, j, lastPostToThread, len, m, mi, open, post, postID, postsCount, ref, ref1, ref2, ref3, seconds, threadID; if (this !== QR.req) { return; } @@ -24823,9 +24745,13 @@ QR = (function() { QR.cooldown.auto = QR.captcha.isEnabled || connErr; QR.cooldown.addDelay(post, 2); } - } else if (err.textContent && (m = err.textContent.match(/(?:(\d+)\s+minutes?\s+)?(\d+)\s+second/i)) && !/duplicate|hour/i.test(err.textContent)) { + } else if (err.textContent && (m = err.textContent.match(/\d+\s+(?:minute|second)/gi)) && !/duplicate|hour/i.test(err.textContent)) { QR.cooldown.auto = !/have\s+been\s+muted/i.test(err.textContent); - seconds = 60 * (+(m[1] || 0)) + (+m[2]); + seconds = 0; + for (j = 0, len = m.length; j < len; j++) { + mi = m[j]; + seconds += (/minute/i.test(mi) ? 60 : 1) * (+mi.match(/\d+/)[0]); + } if (/muted/i.test(err.textContent)) { QR.cooldown.addMute(seconds); } else { @@ -24858,10 +24784,10 @@ QR = (function() { postsCount = QR.posts.length - 1; QR.cooldown.auto = postsCount && isReply; lastPostToThread = !((function() { - var j, len, p, ref4; + var k, len1, p, ref4; ref4 = QR.posts.slice(1); - for (j = 0, len = ref4.length; j < len; j++) { - p = ref4[j]; + for (k = 0, len1 = ref4.length; k < len1; k++) { + p = ref4[k]; if (p.thread === post.thread) { return true; } @@ -25102,6 +25028,13 @@ QR = (function() { }); }); }, + clear: function() { + QR.cooldown.data = $.dict(); + QR.cooldown.changes = $.dict(); + QR.cooldown.auto = false; + QR.cooldown.update(); + return $.queueTask($["delete"], 'cooldowns'); + }, update: function() { var base, cooldown, cooldowns, elapsed, i, len, maxDelay, nCooldowns, now, ref, ref1, save, scope, seconds, start, suffix, threadID, type, update; if (!QR.cooldown.isCounting) { @@ -25524,9 +25457,7 @@ QR = (function() { draggable: true, href: 'javascript:;' }); - $.extend(el, { - innerHTML: "" - }); + $.extend(el, {innerHTML: ""}); this.nodes = { el: el, rm: el.firstChild, @@ -25763,9 +25694,7 @@ QR = (function() { div = $.el('div', { className: className }); - $.extend(div, { - innerHTML: E(message) + ((link) ? " [More info]" : "") + "
[delete post] [delete all]" - }); + $.extend(div, {innerHTML: E(message) + ((link) ? " [More info]" : "") + "
[delete post] [delete all]"}); (this.errors || (this.errors = [])).push(div); ref = $$('a', div), rm = ref[0], rmAll = ref[1]; $.on(div, 'click', (function(_this) { @@ -26568,16 +26497,12 @@ QuoteThreading = if (!(Conf['Quote Threading'] && g.VIEW === 'thread')) { return; } - this.controls = $.el('label', { - innerHTML: " Threading" - }); + this.controls = $.el('label', {innerHTML: " Threading"}); this.threadNewLink = $.el('span', { className: 'brackets-wrap threadnewlink', hidden: true }); - $.extend(this.threadNewLink, { - innerHTML: "Thread New Posts" - }); + $.extend(this.threadNewLink, {innerHTML: "Thread New Posts"}); this.input = $('input', this.controls); this.input.checked = Conf['Thread Quotes']; $.on(this.input, 'change', this.setEnabled); @@ -26872,9 +26797,7 @@ QuoteYou = (function() { var input, label, ref; label = $.el('label', { className: 'toggle-you' - }, { - innerHTML: " You" - }); + }, {innerHTML: " You"}); input = $('input', label); $.on(input, 'change', QuoteYou.menu.toggle); return (ref = Menu.menu) != null ? ref.addEntry({ @@ -27276,6 +27199,7 @@ Main = (function() { if ($.cantSet) { } else if (items.previousversion == null) { + Main.isFirstRun = true; Main.ready(function() { $.set('previousversion', g.VERSION); return Settings.open(); @@ -27299,9 +27223,7 @@ Main = (function() { return $.set(changes, function() { var el, ref; if ((ref = items['Show Updated Notifications']) != null ? ref : true) { - el = $.el('span', { - innerHTML: "4chan X has been updated to version " + E(g.VERSION) + "." - }); + el = $.el('span', {innerHTML: "4chan X has been updated to version " + E(g.VERSION) + "."}); return new Notice('info', el, 15); } }); @@ -27541,9 +27463,7 @@ Main = (function() { return; } if (typeof (base1 = g.SITE).isIncomplete === "function" ? base1.isIncomplete() : void 0) { - msg = $.el('div', { - innerHTML: "The page didn't load completely.
Some features may not work unless you reload." - }); + msg = $.el('div', {innerHTML: "The page didn't load completely.
Some features may not work unless you reload."}); $.on($('a', msg), 'click', function() { return location.reload(); }); @@ -27627,7 +27547,8 @@ Main = (function() { err = error1; errors.push({ message: "Parsing of Post No." + (postRoot.id.match(/\d+/)) + " failed. Post will be skipped.", - error: err + error: err, + html: postRoot.outerHTML }); } } @@ -27746,7 +27667,8 @@ Main = (function() { err = error1; errors.push({ message: "Parsing of Catalog Thread No." + ((threadRoot.dataset.id || threadRoot.id).match(/\d+/)) + " failed. Thread will be skipped.", - error: err + error: err, + html: threadRoot.outerHTML }); } } @@ -27810,11 +27732,21 @@ Main = (function() { return softTask(); }, handleErrors: function(errors) { - var div, error, j, len, logs; + var div, enabled, error, j, len, logs, msg; if (d.body && $.hasClass(d.body, 'fourchan_x') && !$.hasClass(doc, 'tainted')) { new Notice('error', 'Error: Multiple copies of 4chan X are enabled.'); $.addClass(doc, 'tainted'); } + if (g.SITE.testNativeExtension && !$.hasClass(doc, 'tainted')) { + enabled = g.SITE.testNativeExtension().enabled; + if (enabled) { + $.addClass(doc, 'tainted'); + if (Conf['Disable Native Extension'] && !Main.isFirstRun) { + msg = $.el('div', {innerHTML: "Failed to disable the native extension. You may need to block it."}); + new Notice('error', msg); + } + } + } if (!(errors instanceof Array)) { error = errors; } else if (errors.length === 1) { @@ -27824,9 +27756,7 @@ Main = (function() { new Notice('error', Main.parseError(error, Main.reportLink([error])), 15); return; } - div = $.el('div', { - innerHTML: E(errors.length) + " errors occurred." + (Main.reportLink(errors)).innerHTML + " [show]" - }); + div = $.el('div', {innerHTML: E(errors.length) + " errors occurred." + (Main.reportLink(errors)).innerHTML + " [show]"}); $.on(div.lastElementChild, 'click', function() { var ref; return ref = this.textContent === 'show' ? ['hide', false] : ['show', true], this.textContent = ref[0], logs.hidden = ref[1], ref; @@ -27843,9 +27773,7 @@ Main = (function() { parseError: function(data, reportLink) { var context, error, lines, message, ref, ref1; c.error(data.message, data.error.stack); - message = $.el('div', { - innerHTML: E(data.message) + ((reportLink) ? (reportLink).innerHTML : "") - }); + message = $.el('div', {innerHTML: E(data.message) + ((reportLink) ? (reportLink).innerHTML : "")}); error = $.el('div', { textContent: (data.error.name || 'Error') + ": " + (data.error.message || 'see console for details') }); @@ -27856,7 +27784,7 @@ Main = (function() { return [message, error, context]; }, reportLink: function(errors) { - var addDetails, data, details, title, url; + var addDetails, data, details, info, title, url; data = errors[0]; title = data.message; if (errors.length > 1) { @@ -27868,7 +27796,10 @@ Main = (function() { return details += text + '\n'; } }; - addDetails("[Please describe the steps needed to reproduce this error.]\n\nScript: 4chan X ccd0 v" + g.VERSION + " " + $.platform + "\nUser agent: " + navigator.userAgent + "\nURL: " + location.href); + addDetails("[Please describe the steps needed to reproduce this error.]\n\nScript: 4chan X ccd0 v" + g.VERSION + " " + $.platform + "\nURL: " + location.href + "\nUser agent: " + navigator.userAgent); + if ($.platform === 'userscript' && (info = typeof GM !== "undefined" && GM !== null ? GM.info : (typeof GM_info !== "undefined" && GM_info !== null ? GM_info : void 0))) { + addDetails("Userscript manager: " + info.scriptHandler + " " + info.version); + } addDetails('\n' + data.error); if (data.error.stack) { addDetails(data.error.stack.replace(data.error.toString(), '').trim()); @@ -27877,10 +27808,8 @@ Main = (function() { addDetails('\n`' + data.html + '`'); } details = details.replace(/file:\/{3}.+\//g, ''); - url = "https://gitreports.com/issue/ccd0/4chan-x?issue_title=" + (encodeURIComponent(title)) + "&details=" + (encodeURIComponent(details)); - return { - innerHTML: " [report]" - }; + url = 'https://gitreports.com/issue/ccd0/4chan-x?issue_title=%title&details=%details'.replace('%title', encodeURIComponent(title)).replace('%details', encodeURIComponent(details)); + return {innerHTML: " [report]"}; }, isThisPageLegit: function() { if (!('thisPageIsLegit' in Main)) { diff --git a/builds/4chan-X-noupdate.crx b/builds/4chan-X-noupdate.crx index 205c68a46..23cbcfaf6 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 3edbff31f..b3accbeb0 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.11.1 +// @version 1.14.12.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -199,7 +199,7 @@ docSet = function() { }; g = { - VERSION: '1.14.11.1', + VERSION: '1.14.12.0', NAMESPACE: '4chan X.', sites: Object.create(null), boards: Object.create(null) @@ -575,7 +575,8 @@ Config = (function() { ], fourchanImageHost: 'i.4cdn.org', hiddenPSAList: [{}], - 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' + 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: [[]] }; return Config; @@ -2017,8 +2018,8 @@ div[data-checked=\"false\"] > .suboption-list {\n\ }\n\ #fourchanx-settings textarea {\n\ font-family: monospace;\n\ - min-width: 100%;\n\ - max-width: 100%;\n\ + width: 100%;\n\ + resize: vertical;\n\ }\n\ #fourchanx-settings code {\n\ color: #000;\n\ @@ -5247,23 +5248,23 @@ $ = (function() { } })(); - $.globalEval = function(code, data) { - var script; - script = $.el('script', { - textContent: code - }); - if (data) { - $.extend(script.dataset, data); - } - $.add(d.head || doc, script); - return $.rm(script); - }; - $.global = function(fn, data) { + var script; if (doc) { - return $.globalEval("(" + fn + ")();", data); + script = $.el('script', { + textContent: "(" + fn + ").call(document.currentScript.dataset);" + }); + if (data) { + $.extend(script.dataset, data); + } + $.add(d.head || doc, script); + $.rm(script); + return script.dataset; } else { - return fn(); + try { + fn.call(data); + } catch (error) {} + return data; } }; @@ -6627,23 +6628,17 @@ Fetcher = (function() { results1 = []; for (j = l = 0, len1 = ref.length; l < len1; j = ++l) { text2 = ref[j]; - results1.push({ - innerHTML: ((j % 2) ? "" + E(text2) + "" : E(text2)) - }); + results1.push({innerHTML: ((j % 2) ? "" + E(text2) + "" : E(text2))}); } return results1; })(); - text = { - innerHTML: ((greentext) ? "" + E.cat(text) + "" : E.cat(text)) - }; + text = {innerHTML: ((greentext) ? "" + E.cat(text) + "" : E.cat(text))}; results.push(text); } } return results; }).call(this); - comment = { - innerHTML: E.cat(comment) - }; + comment = {innerHTML: E.cat(comment)}; this.threadID = +data.thread_num; o = { ID: this.postID, @@ -6734,71 +6729,29 @@ Fetcher = (function() { }; Fetcher.prototype.archiveTags = { - '\n': { - innerHTML: "
" - }, - '[b]': { - innerHTML: "" - }, - '[/b]': { - innerHTML: "" - }, - '[spoiler]': { - innerHTML: "" - }, - '[/spoiler]': { - innerHTML: "" - }, - '[code]': { - innerHTML: "
"
-      },
-      '[/code]': {
-        innerHTML: "
" - }, - '[moot]': { - innerHTML: "
" - }, - '[/moot]': { - innerHTML: "
" - }, - '[banned]': { - innerHTML: "" - }, - '[/banned]': { - innerHTML: "" - }, + '\n': {innerHTML: "
"}, + '[b]': {innerHTML: ""}, + '[/b]': {innerHTML: ""}, + '[spoiler]': {innerHTML: ""}, + '[/spoiler]': {innerHTML: ""}, + '[code]': {innerHTML: "
"},
+      '[/code]': {innerHTML: "
"}, + '[moot]': {innerHTML: "
"}, + '[/moot]': {innerHTML: "
"}, + '[banned]': {innerHTML: ""}, + '[/banned]': {innerHTML: ""}, '[fortune]': function(text) { - return { - innerHTML: "" - }; + return {innerHTML: ""}; }, - '[/fortune]': { - innerHTML: "" - }, - '[i]': { - innerHTML: "" - }, - '[/i]': { - innerHTML: "" - }, - '[red]': { - innerHTML: "" - }, - '[/red]': { - innerHTML: "" - }, - '[green]': { - innerHTML: "" - }, - '[/green]': { - innerHTML: "" - }, - '[blue]': { - innerHTML: "" - }, - '[/blue]': { - innerHTML: "" - } + '[/fortune]': {innerHTML: ""}, + '[i]': {innerHTML: ""}, + '[/i]': {innerHTML: ""}, + '[red]': {innerHTML: ""}, + '[/red]': {innerHTML: ""}, + '[green]': {innerHTML: ""}, + '[/green]': {innerHTML: ""}, + '[blue]': {innerHTML: ""}, + '[/blue]': {innerHTML: ""} }; return Fetcher; @@ -6819,9 +6772,7 @@ Notice = (function() { this.onclose = onclose; this.close = bind(this.close, this); this.add = bind(this.add, this); - this.el = $.el('div', { - innerHTML: "
" - }); + this.el = $.el('div', {innerHTML: "
"}); this.el.style.opacity = 0; this.setType(type); $.on(this.el.firstElementChild, 'click', this.close); @@ -7115,8 +7066,8 @@ Post = (function() { } $.extend(file, { url: file.link.href, - isImage: /(jpe?g|png|gif|bmp)$/i.test(file.link.href), - isVideo: /(webm|mp4)$/i.test(file.link.href) + isImage: /\.(jpe?g|png|gif|bmp)$/i.test(file.link.href), + isVideo: /\.(webm|mp4)$/i.test(file.link.href) }); size = +file.size.match(/[\d.]+/)[0]; unit = ['B', 'KB', 'MB', 'GB'].indexOf(file.size.match(/\w+$/)[0]); @@ -8329,6 +8280,13 @@ SW = {}; }, isLinkified: function(link) { return ImageHost.test(link.hostname); + }, + testNativeExtension: function() { + return $.global(function() { + if (window.Parser.postMenuIcon) { + return this.enabled = 'true'; + } + }); } }; @@ -8508,9 +8466,7 @@ SW = {}; url = Build.threadURL(boardID, threadID); postLink = url + "#p" + ID; quoteLink = Build.sameThread(boardID, threadID) ? "javascript:quote('" + (+ID) + "');" : url + "#q" + ID; - postInfo = { - innerHTML: "
" + ((!o.isReply || boardID === "f" || subject) ? "" + E(subject || "") + " " : "") + "" + ((email) ? "" : "") + "" + E(name) + "" + ((tripcode) ? " " + E(tripcode) + "" : "") + ((o.extra.xa19s) ? " " + E(o.extra.xa19s) + "" : "") + ((pass) ? " " : "") + ((capcode) ? " ## " + E(capcode) + "" : "") + ((email) ? "" : "") + ((boardID === "f" && !o.isReply || capcodeDescription) ? "" : " ") + ((capcodeDescription) ? " \""" : "") + ((uniqueID && !capcode) ? " (ID: " + E(uniqueID) + ")" : "") + ((flagCode) ? " " : "") + ((flagCodeTroll) ? " \""" : "") + " " + E(dateText) + " No." + E(ID) + "" + ((o.extra.xa19l && o.isReply) ? " Like! ×" + E(o.extra.xa19l) + "" : "") + ((o.isSticky) ? " \"Sticky\"" : "") + ((o.isClosed && !o.isArchived) ? " \"Closed\"" : "") + ((o.isArchived) ? " \"Archived\"" : "") + ((!o.isReply && g.VIEW === "index") ? "   [Reply]" : "") + "
" - }; + postInfo = {innerHTML: "
" + ((!o.isReply || boardID === "f" || subject) ? "" + E(subject || "") + " " : "") + "" + ((email) ? "" : "") + "" + E(name) + "" + ((tripcode) ? " " + E(tripcode) + "" : "") + ((o.extra.xa19s) ? " " + E(o.extra.xa19s) + "" : "") + ((pass) ? " " : "") + ((capcode) ? " ## " + E(capcode) + "" : "") + ((email) ? "" : "") + ((boardID === "f" && !o.isReply || capcodeDescription) ? "" : " ") + ((capcodeDescription) ? " \""" : "") + ((uniqueID && !capcode) ? " (ID: " + E(uniqueID) + ")" : "") + ((flagCode) ? " " : "") + ((flagCodeTroll) ? " \""" : "") + " " + E(dateText) + " No." + E(ID) + "" + ((o.extra.xa19l && o.isReply) ? " Like! ×" + E(o.extra.xa19l) + "" : "") + ((o.isSticky) ? " \"Sticky\"" : "") + ((o.isClosed && !o.isArchived) ? " \"Closed\"" : "") + ((o.isArchived) ? " \"Archived\"" : "") + ((!o.isReply && g.VIEW === "index") ? "   [Reply]" : "") + "
"}; /* File Info */ if (file) { @@ -8519,15 +8475,11 @@ SW = {}; shortFilename = Build.shortFilename(file.name); fileThumb = file.isSpoiler ? Build.spoilerThumb(boardID) : file.thumbURL.replace(protocol, ''); } - fileBlock = { - innerHTML: ((file) ? "
" + ((boardID === "f") ? "
File: " + E(file.name) + "-(" + E(file.size) + ", " + E(file.dimensions) + ((file.tag) ? ", " + E(file.tag) : "") + ")
" : "
File: " + ((file.isSpoiler) ? "Spoiler Image" : E(shortFilename)) + " (" + E(file.size) + ", " + E(file.dimensions || "PDF") + ")
\""") + "
" : ((o.fileDeleted) ? "
\"File
" : "")) - }; + fileBlock = {innerHTML: ((file) ? "
" + ((boardID === "f") ? "
File: " + E(file.name) + "-(" + E(file.size) + ", " + E(file.dimensions) + ((file.tag) ? ", " + E(file.tag) : "") + ")
" : "
File: " + ((file.isSpoiler) ? "Spoiler Image" : E(shortFilename)) + " (" + E(file.size) + ", " + E(file.dimensions || "PDF") + ")
\""") + "
" : ((o.fileDeleted) ? "
\"File
" : ""))}; /* Whole Post */ postClass = o.isReply ? 'reply' : 'op'; - wholePost = { - innerHTML: ((o.isReply) ? "
>>
" : "") + "
" + ((o.isReply) ? (postInfo).innerHTML + (fileBlock).innerHTML : (fileBlock).innerHTML + (postInfo).innerHTML) + "
" + (commentHTML).innerHTML + "
" - }; + wholePost = {innerHTML: ((o.isReply) ? "
>>
" : "") + "
" + ((o.isReply) ? (postInfo).innerHTML + (fileBlock).innerHTML : (fileBlock).innerHTML + (postInfo).innerHTML) + "
" + (commentHTML).innerHTML + "
"}; container = $.el('div', { className: "postContainer " + postClass + "Container", id: "pc" + ID @@ -8618,9 +8570,7 @@ SW = {}; } postCount = data.replies + 1; fileCount = data.images + !!data.ext; - container = $.el('div', { - innerHTML: "
" + E(postCount) + " / " + E(fileCount) + " / " + E(pageCount) + "" + ((thread.isSticky) ? "" : "") + ((thread.isClosed) ? "" : "") + "
" - }); + container = $.el('div', {innerHTML: "
" + E(postCount) + " / " + E(fileCount) + " / " + E(pageCount) + "" + ((thread.isSticky) ? "" : "") + ((thread.isClosed) ? "" : "") + "
"}); $.before(thread.OP.nodes.info, slice.call(container.childNodes)); ref = $$('br', thread.OP.nodes.comment); for (i = 0, len = ref.length; i < len; i++) { @@ -8661,9 +8611,7 @@ SW = {}; link = Build.postURL(thread.board.ID, thread.ID, data.no); return $.el('div', { className: 'catalog-reply' - }, { - innerHTML: ": " + E(excerpt) + "..." - }); + }, {innerHTML: ": " + E(excerpt) + "..."}); } }; @@ -9077,7 +9025,6 @@ Filter = (function() { Filter = { filters: $.dict(), - results: $.dict(), 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' || ref === 'catalog') && Conf['Filter'])) { @@ -9472,7 +9419,7 @@ Filter = (function() { return ta.focus(); }); }, - quickFilterMD5: function() { + quickFilterMD5: function(e) { var files, filter, links, msg, notice, origin, post; post = Get.postFromNode(this); files = post.files.filter(function(f) { @@ -9491,15 +9438,19 @@ Filter = (function() { } else if (g.VIEW === 'index') { ThreadHiding.hide(origin.thread); } + if (e.type !== 'keydown') { + if (post.nodes.post.getBoundingClientRect().height) { + new Notice('info', 'MD5 filtered.', 2); + } + return; + } notice = Filter.quickFilterMD5.notice; if (notice) { notice.filters.push(filter); notice.posts.push(origin); return $('span', notice.el).textContent = notice.filters.length + " MD5s filtered."; } else { - msg = $.el('div', { - innerHTML: "MD5 filtered. [show] [undo]" - }); + msg = $.el('div', {innerHTML: "MD5 filtered. [show] [undo]"}); notice = Filter.quickFilterMD5.notice = new Notice('info', msg, void 0, function() { return delete Filter.quickFilterMD5.notice; }); @@ -10204,9 +10155,7 @@ ThreadHiding = (function() { className: type + "-thread-button", href: 'javascript:;' }); - $.extend(a, { - innerHTML: "" - }); + $.extend(a, {innerHTML: ""}); a.dataset.fullID = thread.fullID; $.on(a, 'click', ThreadHiding.toggle); return a; @@ -10524,13 +10473,22 @@ Header = (function() { Header = { init: function() { var barFixedToggler, barPositionToggler, box, cs, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler; + $.onExists(doc, 'body', (function(_this) { + return function() { + if (!Main.isThisPageLegit()) { + return; + } + $.add(_this.bar, [_this.noticesRoot, _this.toggle]); + $.prepend(d.body, _this.bar); + $.add(d.body, Header.hover); + return _this.setBarPosition(Conf['Bottom Header']); + }; + })(this)); this.menu = new UI.Menu('header'); menuButton = $.el('span', { className: 'menu-button' }); - $.extend(menuButton, { - innerHTML: "" - }); + $.extend(menuButton, {innerHTML: ""}); box = UI.checkbox; barFixedToggler = box('Fixed Header', 'Fixed Header'); headerToggler = box('Header auto-hide', 'Auto-hide header'); @@ -10606,16 +10564,6 @@ Header = (function() { $.on(window, 'load popstate', Header.hashScroll); $.on(d, 'CreateNotification', this.createNotification); this.setBoardList(); - $.onExists(doc, 'body', (function(_this) { - return function() { - if (!Main.isThisPageLegit()) { - return; - } - $.prepend(d.body, _this.bar); - $.add(d.body, Header.hover); - return _this.setBarPosition(Conf['Bottom Header']); - }; - })(this)); $.onExists(doc, g.SITE.selectors.boardList + " + *", Header.generateFullBoardList); Main.ready(function() { var a, absbot, footer, j, len, ref; @@ -10628,7 +10576,9 @@ Header = (function() { $('#navtopright', footer).id = 'navbotright'; $('#settingsWindowLink', footer).id = 'settingsWindowLinkBot'; $.before(absbot, footer); - $.globalEval('window.cloneTopNav = function() {};'); + $.global(function() { + return window.cloneTopNav = function() {}; + }); } if ((Header.bottomBoardList = $(g.SITE.selectors.boardListBottom))) { ref = $$('a', Header.bottomBoardList); @@ -10679,12 +10629,10 @@ Header = (function() { Header.boardList = boardList = $.el('span', { id: 'board-list' }); - $.extend(boardList, { - innerHTML: "" - }); + $.extend(boardList, {innerHTML: ""}); btn = $('.hide-board-list-button', boardList); $.on(btn, 'click', Header.toggleBoardList); - $.add(Header.bar, [Header.boardList, Header.shortcuts, Header.noticesRoot, Header.toggle]); + $.prepend(Header.bar, [Header.boardList, Header.shortcuts]); Header.setCustomNav(Conf['Custom Board Navigation']); Header.generateBoardList(Conf['boardnav']); $.sync('Custom Board Navigation', Header.setCustomNav); @@ -10971,8 +10919,10 @@ Header = (function() { return Header.previousOffset = offsetY; }, setBarPosition: function(bottom) { - var args; - Header.barPositionToggler.checked = bottom; + var args, ref; + if ((ref = Header.barPositionToggler) != null) { + ref.checked = bottom; + } $.event('CloseMenu'); args = bottom ? ['bottom-header', 'top-header', 'after'] : ['top-header', 'bottom-header', 'add']; $.addClass(doc, args[0]); @@ -11156,9 +11106,7 @@ Header = (function() { case 'denied': return; } - el = $.el('span', { - innerHTML: "4chan X needs your permission to show desktop notifications. [FAQ]
or " - }); + el = $.el('span', {innerHTML: "4chan X needs your permission to show desktop notifications. [FAQ]
or "}); ref = $$('button', el), authorize = ref[0], disable = ref[1]; $.on(authorize, 'click', function() { return Notification.requestPermission(function(status) { @@ -11281,9 +11229,7 @@ Index = (function() { this.navLinks = $.el('div', { className: 'navLinks json-index' }); - $.extend(this.navLinks, { - innerHTML: "Index Catalog Archive Bottom ×" - }); + $.extend(this.navLinks, {innerHTML: "Index Catalog Archive Bottom ×"}); $('.cataloglink a', this.navLinks).href = CatalogLinks.catalog(); if (!BoardConfig.isArchived(g.BOARD.ID)) { $('.archlistlink', this.navLinks).hidden = true; @@ -11331,9 +11277,7 @@ Index = (function() { this.pagelist = $.el('div', { className: 'pagelist json-index' }); - $.extend(this.pagelist, { - innerHTML: "
" - }); + $.extend(this.pagelist, {innerHTML: "
"}); $('.cataloglink a', this.pagelist).href = CatalogLinks.catalog(); $.on(this.pagelist, 'click', this.cb.pageNav); this.update(true); @@ -11421,9 +11365,7 @@ Index = (function() { el: $.el('a', { href: 'javascript:;', className: 'has-shortcut-text' - }, { - innerHTML: "Shift+click" - }), + }, {innerHTML: "Shift+click"}), order: 20, open: function(arg) { var thread; @@ -12084,7 +12026,7 @@ Index = (function() { return PostHiding.isHidden(g.BOARD.ID, threadID, replyData.no) || Filter.isHidden(g.SITE.Build.parseJSON(replyData, g.BOARD)); }, buildThreads: function(threadIDs, isCatalog, withReplies) { - var ID, OP, err, errors, isStale, k, lastPost, len1, newPosts, newThreads, obj, t, thread, threadData, threads; + var ID, OP, err, errors, isStale, k, lastPost, len1, newPosts, newThreads, obj, opRoot, t, thread, threadData, threads; threads = []; newThreads = []; newPosts = []; @@ -12119,7 +12061,8 @@ Index = (function() { thread.setPage(Math.floor(Index.threadPosition[ID] / Index.threadsNumPerPage) + 1); } else { obj = Index.parsedThreads[ID]; - OP = new Post(g.SITE.Build.post(obj), thread, g.BOARD); + opRoot = g.SITE.Build.post(obj); + OP = new Post(opRoot, thread, g.BOARD); OP.filterResults = obj.filterResults; newPosts.push(OP); } @@ -12133,7 +12076,8 @@ Index = (function() { } errors.push({ message: "Parsing of Thread No." + thread + " failed. Thread will be skipped.", - error: err + error: err, + html: opRoot != null ? opRoot.outerHTML : void 0 }); } } @@ -12185,7 +12129,8 @@ Index = (function() { } errors.push({ message: "Parsing of Post No." + data.no + " failed. Post will be skipped.", - error: err + error: err, + html: node != null ? node.outerHTML : void 0 }); } } @@ -12570,7 +12515,7 @@ Settings = (function() { $.on(d, 'OpenSettings', function(e) { return Settings.open(e.detail); }); - if (Conf['Disable Native Extension']) { + if (g.SITE.software === 'yotsuba' && Conf['Disable Native Extension']) { if ($.hasStorage) { return $.global(function() { var settings; @@ -12608,9 +12553,7 @@ Settings = (function() { $.event('CloseMenu'); Settings.dialog = dialog = $.el('div', { id: 'overlay' - }, { - innerHTML: "" - }); + }, {innerHTML: ""}); $.on($('.export', dialog), 'click', Settings["export"]); $.on($('.import', dialog), 'click', Settings["import"]); $.on($('.reset', dialog), 'click', Settings.reset); @@ -12699,9 +12642,7 @@ Settings = (function() { boardID: 'qa', threadID: 362590 }); - return cb($.el('li', { - innerHTML: "To protect yourself from malicious ads, you should block ads on 4chan." - })); + return cb($.el('li', {innerHTML: "To protect yourself from malicious ads, you should block ads on 4chan."})); }); }); } @@ -12710,9 +12651,7 @@ Settings = (function() { var addCheckboxes, addWarning, button, div, fs, inputs, items, key, keyFS, obj, ref, ref1, warning, warnings; warnings = $.el('fieldset', { hidden: true - }, { - innerHTML: "Warnings" - }); + }, {innerHTML: "Warnings"}); addWarning = function(item) { $.add($('ul', warnings), item); return warnings.hidden = false; @@ -12735,9 +12674,7 @@ Settings = (function() { continue; } description = arr[1]; - div = $.el('div', { - innerHTML: ": " + E(description) + "" - }); + div = $.el('div', {innerHTML: ": " + E(description) + ""}); div.dataset.name = key; input = $('input', div); $.on(input, 'change', $.cb.checked); @@ -12763,14 +12700,10 @@ Settings = (function() { ref1 = Config.main; for (keyFS in ref1) { obj = ref1[keyFS]; - fs = $.el('fieldset', { - innerHTML: "" + E(keyFS) + "" - }); + fs = $.el('fieldset', {innerHTML: "" + E(keyFS) + ""}); addCheckboxes(fs, obj); if (keyFS === 'Posting and Captchas') { - $.add(fs, $.el('p', { - innerHTML: "For more info on captcha options and issues, see the captcha FAQ." - })); + $.add(fs, $.el('p', {innerHTML: "For more info on captcha options and issues, see the captcha FAQ."})); } $.add(section, fs); } @@ -12789,9 +12722,7 @@ Settings = (function() { inputs[key].parentNode.parentNode.dataset.checked = val; } }); - div = $.el('div', { - innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." - }); + div = $.el('div', {innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply."}); button = $('button', div); $.get({ hiddenThreads: $.dict(), @@ -12935,17 +12866,20 @@ Settings = (function() { 'Disable 4chan\'s extension': 'Disable Native Extension', 'Comment Auto-Expansion': '', 'Remove Slug': '', + 'Always HTTPS': 'Redirect to HTTPS', 'Check for Updates': '', 'Recursive Filtering': 'Recursive Hiding', 'Reply Hiding': 'Reply Hiding Buttons', 'Thread Hiding': 'Thread Hiding Buttons', 'Show Stubs': 'Stubs', 'Image Auto-Gif': 'Replace GIF', + 'Expand All WebM': 'Expand videos', 'Reveal Spoilers': 'Reveal Spoiler Thumbnails', 'Expand From Current': 'Expand from here', 'Current Page': 'Page Count in Stats', 'Current Page Position': '', 'Alternative captcha': 'Use Recaptcha v1', + 'Alt index captcha': 'Use Recaptcha v1 on Index', 'Auto Submit': 'Post on Captcha Completion', 'Open Reply in New Tab': 'Open Post in New Tab', 'Remember QR size': 'Remember QR Size', @@ -12967,6 +12901,7 @@ Settings = (function() { 'spoiler': 'Spoiler tags', 'sageru': 'Toggle sage', 'code': 'Code tags', + 'sjis': 'SJIS tags', 'submit': 'Submit QR', 'watch': 'Watch', 'update': 'Update', @@ -12987,6 +12922,10 @@ Settings = (function() { 'Scrolling': 'Auto Scroll', 'Verbose': '' }); + if ('Always CDN' in data.Conf) { + data.Conf['fourchanImageHost'] = data.Conf['Always CDN'] ? 'i.4cdn.org' : ''; + delete data.Conf['Always CDN']; + } data.Conf.sauces = data.Conf.sauces.replace(/\$\d/g, function(c) { switch (c) { case '$1': @@ -13013,15 +12952,17 @@ Settings = (function() { } } if (data.WatchedThreads) { - data.Conf['watchedThreads'] = { - boards: $.dict() - }; + data.Conf['watchedThreads'] = $.dict.clone({ + '4chan.org': { + boards: {} + } + }); ref1 = data.WatchedThreads; for (boardID in ref1) { threads = ref1[boardID]; for (threadID in threads) { threadData = threads[threadID]; - ((base = data.Conf['watchedThreads'].boards)[boardID] || (base[boardID] = $.dict()))[threadID] = { + ((base = data.Conf['watchedThreads']['4chan.org'].boards)[boardID] || (base[boardID] = $.dict()))[threadID] = { excerpt: threadData.textContent }; } @@ -13361,9 +13302,7 @@ Settings = (function() { }, filter: function(section) { var select; - $.extend(section, { - innerHTML: "
" - }); + $.extend(section, {innerHTML: "
"}); select = $('select', section); $.on(select, 'change', Settings.selectFilter); return Settings.selectFilter.call(select); @@ -13391,20 +13330,14 @@ Settings = (function() { filterTypes = Object.keys(Config.filter).filter(function(x) { return x !== 'general'; }).map(function(x, i) { - return { - innerHTML: ((i) ? "," : "") + "" + E(x) - }; - }); - $.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;.
" + return {innerHTML: ((i) ? "," : "") + "" + E(x)}; }); + $.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;.
"}); return $('.warning', div).hidden = Conf['Filter']; }, sauce: function(section) { var ta; - $.extend(section, { - innerHTML: "
Sauce is disabled.
These parameters will be replaced by their corresponding values in the URL and displayed text:
  • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
  • %URL: Full image URL.
  • %TURL: Thumbnail URL.
  • %name: Original file name.
  • %board: Current board.
  • %MD5: MD5 hash in base64.
  • %sMD5: MD5 hash in base64 using - and _.
  • %hMD5: MD5 hash in hexadecimal.
  • %$0: Matched regular expression within the filename.
  • %$1, %$2, %$3, ... : Subexpressions within the matched regular expression.
  • %%, %semi: Literal % and ;.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards/sites by appending ;boards:[board1],[board2]. See the Filter guide for details.
You can specify the applicable file types by appending ;types:[extension1],[extension2].
You can specify a regular expression the filename must match by appending ;regexp:[regular expression].
" - }); + $.extend(section, {innerHTML: "
Sauce is disabled.
These parameters will be replaced by their corresponding values in the URL and displayed text:
  • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
  • %URL: Full image URL.
  • %TURL: Thumbnail URL.
  • %name: Original file name.
  • %board: Current board.
  • %MD5: MD5 hash in base64.
  • %sMD5: MD5 hash in base64 using - and _.
  • %hMD5: MD5 hash in hexadecimal.
  • %$0: Matched regular expression within the filename.
  • %$1, %$2, %$3, ... : Subexpressions within the matched regular expression.
  • %%, %semi: Literal % and ;.
Lines starting with a # will be ignored.
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards/sites by appending ;boards:[board1],[board2]. See the Filter guide for details.
You can specify the applicable file types by appending ;types:[extension1],[extension2].
You can specify a regular expression the filename must match by appending ;regexp:[regular expression].
"}); $('.warning', section).hidden = Conf['Sauce']; ta = $('textarea', section); $.get('sauces', Conf['sauces'], function(item) { @@ -13415,9 +13348,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"
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.
:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
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.

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, options (or equivalently email), subject and password.
  • Wrap values of items with quotes, like this: options:"sage".
  • Force values as defaults with the always keyword, for example: options:"sage";always.
  • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
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:
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"
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.
:
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
Hour: %k, %H, %l, %I, %p, %P
Minute: %M
Second: %S
Literal %: %%
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.

    You can use these settings with each item, separate them with semicolons:
  • Possible items are: name, options (or equivalently email), subject and password.
  • Wrap values of items with quotes, like this: options:"sage".
  • Force values as defaults with the always keyword, for example: options:"sage";always.
  • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
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]; @@ -13607,9 +13538,7 @@ Settings = (function() { textContent: archive[1] })); } - $.extend(td, { - innerHTML: "" - }); + $.extend(td, {innerHTML: ""}); select = td.firstElementChild; if (!(select.disabled = length === 1)) { select.setAttribute('data-boardid', boardID); @@ -13731,9 +13660,7 @@ Settings = (function() { }, keybinds: function(section) { var arr, input, inputs, items, key, ref, tbody, tr; - $.extend(section, { - innerHTML: "
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
" - }); + $.extend(section, {innerHTML: "
Keybinds are disabled.
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
Press Backspace to disable a keybind.
ActionsKeybinds
"}); $('.warning', section).hidden = Conf['Keybinds']; tbody = $('tbody', section); items = $.dict(); @@ -13741,9 +13668,7 @@ Settings = (function() { ref = Config.hotkeys; for (key in ref) { arr = ref[key]; - tr = $.el('tr', { - innerHTML: "" + E(arr[1]) + "" - }); + tr = $.el('tr', {innerHTML: "" + E(arr[1]) + ""}); input = $('input', tr); input.name = key; input.spellcheck = false; @@ -14424,9 +14349,7 @@ Gallery = (function() { nodes.el = dialog = $.el('div', { id: 'a-gallery' }); - $.extend(dialog, { - innerHTML: "
× /
" - }); + $.extend(dialog, {innerHTML: "
× /
"}); ref = { buttons: '.gal-buttons', frame: '.gal-image', @@ -14875,9 +14798,7 @@ Gallery = (function() { } return results; })(); - delayLabel = $.el('label', { - innerHTML: "Slide Delay: " - }); + delayLabel = $.el('label', {innerHTML: "Slide Delay: "}); delayInput = delayLabel.firstElementChild; delayInput.value = Gallery.delay; $.on(delayInput, 'change', Gallery.cb.setDelay); @@ -14991,7 +14912,7 @@ ImageCommon = (function() { onloadend: function() { var i, len, postObj, ref, ref1; if (this.status === 404) { - post.kill(!post.isClone); + post.kill(!post.isClone, fileObj.index); } if (this.status !== 200) { return redirect(); @@ -15083,9 +15004,7 @@ ImageExpand = (function() { this.videoControls = $.el('span', { className: 'video-controls' }); - $.extend(this.videoControls, { - innerHTML: " contract" - }); + $.extend(this.videoControls, {innerHTML: " contract"}); return Callbacks.Post.push({ name: 'Image Expansion', cb: this.node @@ -15693,9 +15612,7 @@ ImageLoader = (function() { if (!(Conf['Image Prefetching'] && ((ref1 = g.VIEW) === 'index' || ref1 === 'thread'))) { return; } - prefetch = $.el('label', { - innerHTML: " Prefetch Images" - }); + prefetch = $.el('label', {innerHTML: " Prefetch Images"}); this.el = prefetch.firstElementChild; $.on(this.el, 'change', this.toggle); return Header.menu.addEntry({ @@ -15741,12 +15658,19 @@ ImageLoader = (function() { return file.videoThumb = true; }, prefetch: function(post, file) { - var clone, el, i, isImage, isVideo, len, match, ref, replace, thumb, type, url; + var clone, el, i, isImage, isVideo, len, ref, ref1, replace, thumb, type, url; isImage = file.isImage, isVideo = file.isVideo, thumb = file.thumb, url = file.url; if (file.isPrefetched || !(isImage || isVideo) || post.isHidden || post.thread.isHidden) { return; } - type = (match = url.match(/\.([^.]+)$/)[1].toUpperCase()) === 'JPEG' ? 'JPG' : match; + if (isVideo) { + type = 'WEBM'; + } else { + type = (ref = url.match(/\.([^.]+)$/)) != null ? ref[1].toUpperCase() : void 0; + if (type === 'JPEG') { + type = 'JPG'; + } + } replace = Conf["Replace " + type] && !/spoiler/.test(thumb.src || thumb.dataset.src); if (!(replace || Conf['prefetch'])) { return; @@ -15761,9 +15685,9 @@ ImageLoader = (function() { } file.isPrefetched = true; if (file.videoThumb) { - ref = post.clones; - for (i = 0, len = ref.length; i < len; i++) { - clone = ref[i]; + ref1 = post.clones; + for (i = 0, len = ref1.length; i < len; i++) { + clone = ref1[i]; clone.file.thumb.preload = 'auto'; } thumb.preload = 'auto'; @@ -15777,10 +15701,10 @@ ImageLoader = (function() { el = $.el(isImage ? 'img' : 'video'); if (replace && isImage) { $.on(el, 'load', function() { - var j, len1, ref1; - ref1 = post.clones; - for (j = 0, len1 = ref1.length; j < len1; j++) { - clone = ref1[j]; + var j, len1, ref2; + ref2 = post.clones; + for (j = 0, len1 = ref2.length; j < len1; j++) { + clone = ref2[j]; clone.file.thumb.src = url; } return thumb.src = url; @@ -15860,9 +15784,7 @@ Metadata = (function() { className: 'webm-title' }); el.dataset.index = i; - $.extend(el, { - innerHTML: "" - }); + $.extend(el, {innerHTML: ""}); $.add(file.text, [$.tn(' '), el]); } if (el.children.length === 1) { @@ -16249,9 +16171,7 @@ Volume = (function() { volumeEntry = $.el('label', { title: 'Default volume for videos.' }); - $.extend(volumeEntry, { - innerHTML: " Volume" - }); + $.extend(volumeEntry, {innerHTML: " Volume"}); this.inputs = { unmute: unmuteEntry.firstElementChild, volume: volumeEntry.firstElementChild @@ -16361,20 +16281,18 @@ Embedding = (function() { this.types[type.key] = type; } if (Conf['Embedding'] && g.VIEW !== 'archive') { - this.dialog = UI.dialog('embedding', { - innerHTML: "
×
" - }); + this.dialog = UI.dialog('embedding', {innerHTML: "
×
"}); this.media = $('#media-embed', this.dialog); $.one(d, '4chanXInitFinished', this.ready); $.on(d, 'IndexRefreshInternal', function() { return g.posts.forEach(function(post) { - var embed, k, l, len1, len2, ref2, ref3; + var embed, l, len1, len2, n, ref2, ref3; ref2 = [post].concat(slice.call(post.clones)); - for (k = 0, len1 = ref2.length; k < len1; k++) { - post = ref2[k]; + for (l = 0, len1 = ref2.length; l < len1; l++) { + post = ref2[l]; ref3 = post.nodes.embedlinks; - for (l = 0, len2 = ref3.length; l < len2; l++) { - embed = ref3[l]; + for (n = 0, len2 = ref3.length; n < len2; n++) { + embed = ref3[n]; Embedding.cb.catalogRemove.call(embed); } } @@ -16382,7 +16300,7 @@ Embedding = (function() { }); } if (Conf['Link Title']) { - return $.on(d, '4chanXInitFinished PostsInserted', function() { + $.on(d, '4chanXInitFinished PostsInserted', function() { var key, ref2, ref3, service; ref2 = Embedding.types; for (key in ref2) { @@ -16392,6 +16310,8 @@ Embedding = (function() { } } }); + this.cache.init(Conf['cachedTitles']); + return $.sync('cachedTitles', this.cache.sync); } }, events: function(post) { @@ -16464,9 +16384,7 @@ Embedding = (function() { embed = $.el('a', { className: 'embedder', href: 'javascript:;' - }, { - innerHTML: "(unembed)" - }); + }, {innerHTML: "(unembed)"}); ref = { key: key, uid: uid, @@ -16521,13 +16439,15 @@ Embedding = (function() { return style.pointerEvents = 'none'; }, title: function(data) { - var key, link, options, post, service, uid; + var key, link, options, post, service, text, uid; key = data.key, uid = data.uid, options = data.options, link = data.link, post = data.post; if (!(service = Embedding.types[key].title)) { return; } $.addClass(link, key.toLowerCase()); - if (service.batchSize) { + if ((text = Embedding.cache.get(data))) { + return Embedding.cb.titleText(text, data); + } else if (service.batchSize) { (service.queue || (service.queue = [])).push(data); if (service.queue.length >= service.batchSize) { return Embedding.flushTitles(service); @@ -16562,6 +16482,70 @@ Embedding = (function() { return results; })()), cb); }, + cache: (function() { + var get, init, newEntries, save, set, sync, titles; + titles = $.dict(); + newEntries = []; + init = function(data) { + var j, key, len, ref, text, uid; + try { + for (j = 0, len = data.length; j < len; j++) { + ref = data[j], key = ref.key, uid = ref.uid, text = ref.text; + titles[key + "." + uid] = text; + } + } catch (error) {} + }; + sync = function(data) { + var j, k, key, len, ref, text, uid; + try { + for (j = 0, len = data.length; j < len; j++) { + ref = data[j], key = ref.key, uid = ref.uid, text = ref.text; + k = key + "." + uid; + if (k in titles) { + break; + } + titles[k] = text; + } + } catch (error) {} + }; + get = function(arg) { + var key, uid; + key = arg.key, uid = arg.uid; + return titles[key + "." + uid]; + }; + set = function(arg, text) { + var key, uid; + key = arg.key, uid = arg.uid; + titles[key + "." + uid] = text; + newEntries.push({ + key: key, + uid: uid, + text: text + }); + return save(); + }; + save = $.debounce(2 * $.SECOND, function() { + return $.get('cachedTitles', Conf['cachedTitles'], function(arg) { + var cachedTitles; + cachedTitles = arg.cachedTitles; + sync(cachedTitles); + try { + cachedTitles = newEntries.concat(cachedTitles).slice(-100); + } catch (error) { + cachedTitles = newEntries; + } + newEntries = []; + return $.set('cachedTitles', cachedTitles); + }); + }); + return { + init: init, + sync: sync, + get: get, + set: set, + save: save + }; + })(), preview: function(data) { var key, link, service, uid; key = data.key, uid = data.uid, link = data.link; @@ -16627,34 +16611,42 @@ Embedding = (function() { } }, title: function(req, data) { - var base1, j, k, key, len, len1, link, link2, options, post, post2, ref, ref1, service, status, text, uid; + var key, service, status, text, uid; if (!req.status) { return; } - key = data.key, uid = data.uid, options = data.options, link = data.link, post = data.post; + key = data.key, uid = data.uid; status = req.status; service = Embedding.types[key].title; - text = "[" + key + "] " + ((function() { - switch (status) { - case 200: - case 304: - return service.text(req.response, uid); - case 404: - return "Not Found"; - case 403: - return "Forbidden or Private"; - default: - return status + "'d"; - } - })()); + switch (status) { + case 200: + case 304: + text = service.text(req.response, uid); + Embedding.cache.set(data, text); + break; + case 404: + text = "Not Found"; + break; + case 403: + text = "Forbidden or Private"; + break; + default: + text = status + "'d"; + } + return Embedding.cb.titleText(text, data); + }, + titleText: function(text, data) { + var base1, j, key, l, len, len1, link, link2, post, post2, ref, ref1; + key = data.key, link = data.link, post = data.post; + text = "[" + key + "] " + text; link.dataset.original = link.textContent; link.textContent = text; ref = post.clones; for (j = 0, len = ref.length; j < len; j++) { post2 = ref[j]; ref1 = $$('a.linkify', post2.nodes.comment); - for (k = 0, len1 = ref1.length; k < len1; k++) { - link2 = ref1[k]; + for (l = 0, len1 = ref1.length; l < len1; l++) { + link2 = ref1[l]; if (!(link2.href === link.href)) { continue; } @@ -16683,9 +16675,7 @@ Embedding = (function() { regExp: /^[^?#]+\.(?:gif|png|jpg|jpeg|bmp)(?::\w+)?(?:[?#]|$)/i, style: '', el: function(a) { - return $.el('div', { - innerHTML: "" - }); + return $.el('div', {innerHTML: ""}); } }, { key: 'video', @@ -16845,10 +16835,10 @@ Embedding = (function() { } }, { key: 'Loopvid', - regExp: /^\w+:\/\/(?:www\.)?loopvid.appspot.com\/#?((?:pf|kd|lv|gd|gh|db|dx|nn|cp|wu|ig|ky|mf|m2|pc|1c|pi|ni|wl|ko|gc)\/[\w\-\/]+(?:,[\w\-\/]+)*|fc\/\w+\/\d+|https?:\/\/.+)/, + regExp: /^\w+:\/\/(?:www\.)?loopvid.appspot.com\/#?((?:pf|kd|lv|gd|gh|db|dx|nn|cp|wu|ig|ky|mf|m2|pc|1c|pi|ni|wl|ko|mm|ic|gc)\/[\w\-\/]+(?:,[\w\-\/]+)*|fc\/\w+\/\d+|https?:\/\/.+)/, style: 'max-width: 80vw; max-height: 80vh;', el: function(a) { - var _, base, el, host, j, k, l, len, len1, len2, name, names, ref, ref1, type, types, url, urls; + var _, base, el, host, j, l, len, len1, len2, n, name, names, ref, ref1, type, types, url, urls; el = $.el('video', { controls: true, preload: 'auto', @@ -16876,8 +16866,8 @@ Embedding = (function() { ref1 = names.split(','); for (j = 0, len = ref1.length; j < len; j++) { name = ref1[j]; - for (k = 0, len1 = types.length; k < len1; k++) { - type = types[k]; + for (l = 0, len1 = types.length; l < len1; l++) { + type = types[l]; base = "" + name + type; urls = (function() { switch (host) { @@ -16910,25 +16900,29 @@ Embedding = (function() { case 'm2': return ["https://kastden.org/_loopvid_media/m2/" + base]; case 'pc': - return ["http://a.pomf.cat/" + base]; + return ["https://kastden.org/_loopvid_media/pc/" + base, "https://web.archive.org/web/2/http://a.pomf.cat/" + base]; case '1c': return ["http://b.1339.cf/" + base]; case 'pi': - return ["https://u.pomf.is/" + base]; + return ["https://kastden.org/_loopvid_media/pi/" + base, "https://web.archive.org/web/2/https://u.pomf.is/" + base]; case 'ni': - return ["https://u.nya.is/" + base]; + return ["https://kastden.org/_loopvid_media/ni/" + base, "https://web.archive.org/web/2/https://u.nya.is/" + base]; case 'wl': return ["http://webm.land/media/" + base]; case 'ko': return ["https://kordy.kastden.org/loopvid/" + base]; + case 'mm': + return ["https://kastden.org/_loopvid_media/mm/" + base, "https://web.archive.org/web/2/https://my.mixtape.moe/" + base]; + case 'ic': + return ["https://media.8ch.net/file_store/" + base]; case 'fc': return ["//" + (ImageHost.host()) + "/" + base + ".webm"]; case 'gc': return ["https://" + type + ".gfycat.com/" + name + ".webm"]; } })(); - for (l = 0, len2 = urls.length; l < len2; l++) { - url = urls[l]; + for (n = 0, len2 = urls.length; n < len2; n++) { + url = urls[n]; $.add(el, $.el('source', { src: url })); @@ -17567,9 +17561,7 @@ DeleteLink = (function() { } link.textContent = DeleteLink.linkText(fileOnly); if (resDoc.title === '4chan - Banned') { - el = $.el('span', { - innerHTML: "You can't delete posts because you are banned." - }); + el = $.el('span', {innerHTML: "You can't delete posts because you are banned."}); return new Notice('warning', el, 20); } else if (msg = resDoc.getElementById('errmsg')) { new Notice('warning', msg.textContent, 20); @@ -17681,9 +17673,7 @@ Menu = (function() { className: 'menu-button', href: 'javascript:;' }); - $.extend(this.button, { - innerHTML: "" - }); + $.extend(this.button, {innerHTML: ""}); this.menu = new UI.Menu('post'); Callbacks.Post.push({ name: 'Menu', @@ -18542,14 +18532,10 @@ FileInfo = (function() { var a, i, j, len, len1, output, ref, ref1; output = []; formatString.replace(/%(.)|[^%]+/g, function(s, c) { - output.push($.hasOwn(FileInfo.formatters, c) ? FileInfo.formatters[c].call(post) : { - innerHTML: E(s) - }); + output.push($.hasOwn(FileInfo.formatters, c) ? FileInfo.formatters[c].call(post) : {innerHTML: E(s)}); return ''; }); - $.extend(outputNode, { - innerHTML: E.cat(output) - }); + $.extend(outputNode, {innerHTML: E.cat(output)}); ref = $$('.download-button', outputNode); for (i = 0, len = ref.length; i < len; i++) { a = ref[i]; @@ -18563,93 +18549,59 @@ FileInfo = (function() { }, formatters: { t: function() { - return { - innerHTML: E(this.file.url.match(/[^/]*$/)[0]) - }; + return {innerHTML: E(this.file.url.match(/[^/]*$/)[0])}; }, T: function() { - return { - innerHTML: "" + (FileInfo.formatters.t.call(this)).innerHTML + "" - }; + return {innerHTML: "" + (FileInfo.formatters.t.call(this)).innerHTML + ""}; }, l: function() { - return { - innerHTML: "" + (FileInfo.formatters.n.call(this)).innerHTML + "" - }; + return {innerHTML: "" + (FileInfo.formatters.n.call(this)).innerHTML + ""}; }, L: function() { - return { - innerHTML: "" + (FileInfo.formatters.N.call(this)).innerHTML + "" - }; + return {innerHTML: "" + (FileInfo.formatters.N.call(this)).innerHTML + ""}; }, n: function() { var fullname, shortname; fullname = this.file.name; shortname = g.SITE.Build.shortFilename(this.file.name, this.isReply); if (fullname === shortname) { - return { - innerHTML: E(fullname) - }; + return {innerHTML: E(fullname)}; } else { - return { - innerHTML: "" + E(shortname) + "" + E(fullname) + "" - }; + return {innerHTML: "" + E(shortname) + "" + E(fullname) + ""}; } }, N: function() { - return { - innerHTML: E(this.file.name) - }; + return {innerHTML: E(this.file.name)}; }, d: function() { - return { - innerHTML: "" - }; + return {innerHTML: ""}; }, f: function() { - return { - innerHTML: "" - }; + return {innerHTML: ""}; }, p: function() { - return { - innerHTML: ((this.file.isSpoiler) ? "Spoiler, " : "") - }; + return {innerHTML: ((this.file.isSpoiler) ? "Spoiler, " : "")}; }, s: function() { - return { - innerHTML: E(this.file.size) - }; + return {innerHTML: E(this.file.size)}; }, B: function() { - return { - innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes" - }; + return {innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes"}; }, K: function() { - return { - innerHTML: E(Math.round(this.file.sizeInBytes/1024)) + " KB" - }; + return {innerHTML: E(Math.round(this.file.sizeInBytes/1024)) + " KB"}; }, M: function() { - return { - innerHTML: E(Math.round(this.file.sizeInBytes/1048576*100)/100) + " MB" - }; + return {innerHTML: E(Math.round(this.file.sizeInBytes/1048576*100)/100) + " MB"}; }, r: function() { - return { - innerHTML: E(this.file.dimensions || "PDF") - }; + return {innerHTML: E(this.file.dimensions || "PDF")}; }, g: function() { - return { - innerHTML: ((this.file.tag) ? ", " + E(this.file.tag) : "") - }; + return {innerHTML: ((this.file.tag) ? ", " + E(this.file.tag) : "")}; }, '%': function() { - return { - innerHTML: "%" - }; + return {innerHTML: "%"}; } } }; @@ -18718,7 +18670,17 @@ Fourchan = (function() { return $.addClass(pre, 'prettyprinted'); } }); - $.globalEval('window.addEventListener(\'prettyprint\', function(e) {\n window.dispatchEvent(new CustomEvent(\'prettyprint:cb\', {\n detail: {\n ID: e.detail.ID,\n i: e.detail.i,\n html: prettyPrintOne(e.detail.html)\n }\n }));\n}, false);'); + $.global(function() { + return window.addEventListener('prettyprint', function(e) { + return window.dispatchEvent(new CustomEvent('prettyprint:cb', { + detail: { + ID: e.detail.ID, + i: e.detail.i, + html: window.prettyPrintOne(e.detail.html) + } + })); + }, false); + }); Callbacks.Post.push({ name: 'Parse [code] tags', cb: Fourchan.code @@ -19317,7 +19279,7 @@ Keybinds = (function() { } post = Keybinds.post(threadRoot); Keybinds.hl(+1, threadRoot); - Filter.quickFilterMD5.call(post); + Filter.quickFilterMD5.call(post, e); break; case Conf['Previous Post Quoting You']: if (!(threadRoot && QuoteYou.db)) { @@ -19531,34 +19493,24 @@ ModContact = (function() { } }, template: function(capcode) { - return { - innerHTML: "feedback" + (ModContact.specific[capcode]()).innerHTML - }; + return {innerHTML: "feedback" + (ModContact.specific[capcode]()).innerHTML}; }, specific: { Mod: function() { - return { - innerHTML: " IRC" - }; + return {innerHTML: " IRC"}; }, Manager: function() { return ModContact.specific.Mod(); }, Developer: function() { - return { - innerHTML: " github" - }; + return {innerHTML: " github"}; }, Admin: function() { - return { - innerHTML: " twitter" - }; + return {innerHTML: " twitter"}; } }, moveNote: { - qa: { - innerHTML: "Moving a thread to /qa/ does not imply mods will read it. If you wish to contact mods, use feedback (https://www.4chan.org/feedback) or IRC (https://www.4chan-x.net/4chan-irc.html)." - } + qa: {innerHTML: "Moving a thread to /qa/ does not imply mods will read it. If you wish to contact mods, use feedback (https://www.4chan.org/feedback) or IRC (https://www.4chan-x.net/4chan-irc.html)."} } }; @@ -19889,9 +19841,7 @@ PostJumper = (function() { span = $.el('span', { className: 'postJumper' }); - $.extend(span, { - innerHTML: "" + E(charPrev) + "" + E(charNext) + "" - }); + $.extend(span, {innerHTML: "" + E(charPrev) + "" + E(charNext) + ""}); return span; }, scroll: function(fromJumper, toJumper) { @@ -20121,9 +20071,7 @@ Report = (function() { fieldset = $.el('fieldset', { id: 'archive-report', hidden: true - }, { - innerHTML: "" - }); + }, {innerHTML: ""}); enabled = $('#archive-report-enabled', fieldset); reason = $('#archive-report-reason', fieldset); submit = $('#archive-report-submit', fieldset); @@ -20590,9 +20538,7 @@ ReplyPruning = (function() { label = UI.checkbox('Prune Replies', 'Show Last', Conf['Prune All Threads']); el = $.el('span', { title: 'Maximum number of replies to show.' - }, { - innerHTML: " " - }); + }, {innerHTML: " "}); $.prepend(el, label); this.inputs = { enabled: label.firstElementChild, @@ -20736,9 +20682,7 @@ ThreadStats = (function() { if (Conf['Page Count in Stats']) { this[(typeof (base = g.SITE).isPrunedByAge === "function" ? base.isPrunedByAge(g.BOARD) : void 0) ? 'showPurgePos' : 'showPage'] = true; } - statsHTML = { - innerHTML: "? / ?" + ((Conf["IP Count in Stats"] && g.SITE.hasIPCount) ? " / ?" : "") + ((Conf["Page Count in Stats"]) ? " / ?" : "") - }; + statsHTML = {innerHTML: "? / ?" + ((Conf["IP Count in Stats"] && g.SITE.hasIPCount) ? " / ?" : "") + ((Conf["Page Count in Stats"]) ? " / ?" : "")}; statsTitle = 'Posts / Files'; if (Conf['IP Count in Stats'] && g.SITE.hasIPCount) { statsTitle += ' / IPs'; @@ -20754,9 +20698,7 @@ ThreadStats = (function() { $.extend(sc, statsHTML); Header.addShortcut('stats', sc, 200); } else { - this.dialog = sc = UI.dialog('thread-stats', { - innerHTML: "
" + (statsHTML).innerHTML + "
" - }); + this.dialog = sc = UI.dialog('thread-stats', {innerHTML: "
" + (statsHTML).innerHTML + "
"}); $.addClass(doc, 'float'); $.ready(function() { return $.add(d.body, sc); @@ -20925,14 +20867,10 @@ ThreadUpdater = (function() { this.dialog = sc = $.el('span', { id: 'updater' }); - $.extend(sc, { - innerHTML: "" - }); + $.extend(sc, {innerHTML: ""}); Header.addShortcut('updater', sc, 100); } else { - this.dialog = sc = UI.dialog('updater', { - innerHTML: "
" - }); + this.dialog = sc = UI.dialog('updater', {innerHTML: "
"}); $.addClass(doc, 'float'); $.ready(function() { return $.add(d.body, sc); @@ -20946,9 +20884,7 @@ ThreadUpdater = (function() { updateLink = $.el('span', { className: 'brackets-wrap updatelink' }); - $.extend(updateLink, { - innerHTML: "Update" - }); + $.extend(updateLink, {innerHTML: "Update"}); Main.ready(function() { var navLinksBot; if ((navLinksBot = $('.navLinksBot'))) { @@ -20974,9 +20910,7 @@ ThreadUpdater = (function() { el: el }); } - this.settings = $.el('span', { - innerHTML: "Interval" - }); + this.settings = $.el('span', {innerHTML: "Interval"}); $.on(this.settings, 'click', this.intervalShortcut); subEntries.push({ el: this.settings @@ -21359,9 +21293,7 @@ ThreadWatcher = (function() { }); this.db = new DataBoard('watchedThreads', this.refresh, true); this.dbLM = new DataBoard('watcherLastModified', null, true); - this.dialog = UI.dialog('thread-watcher', { - innerHTML: "
Thread Watcher ×
" - }); + this.dialog = UI.dialog('thread-watcher', {innerHTML: "
Thread Watcher ×
"}); this.status = $('#watcher-status', this.dialog); this.list = this.dialog.lastElementChild; this.refreshButton = $('.refresh', this.dialog); @@ -21399,9 +21331,7 @@ ThreadWatcher = (function() { el: $.el('a', { href: 'javascript:;', className: 'has-shortcut-text' - }, { - innerHTML: "Alt+click" - }), + }, {innerHTML: "Alt+click"}), order: 6, open: function(arg) { var thread; @@ -23486,9 +23416,7 @@ Captcha = {}; root = $.el('div', { className: 'captcha-root' }); - $.extend(root, { - innerHTML: "
" - }); + $.extend(root, {innerHTML: "
"}); counter = $('.captcha-counter > a', root); this.nodes = { root: root, @@ -23767,9 +23695,7 @@ PassLink = (function() { passLink = $.el('span', { className: 'brackets-wrap pass-link-container' }); - $.extend(passLink, { - innerHTML: "4chan Pass" - }); + $.extend(passLink, {innerHTML: "4chan Pass"}); $.on(passLink.firstElementChild, 'click', function() { return window.open("//sys." + (location.hostname.split('.')[1]) + ".org/auth", Date.now(), 'width=500,height=280,toolbar=0'); }); @@ -23940,9 +23866,7 @@ QR = (function() { link = $.el('h1', { className: "qr-link-container" }); - $.extend(link, { - innerHTML: "" + ((g.VIEW === "thread") ? "Reply to Thread" : "Start a Thread") + "" - }); + $.extend(link, {innerHTML: "" + ((g.VIEW === "thread") ? "Reply to Thread" : "Start a Thread") + ""}); QR.link = link.firstElementChild; $.on(link.firstChild, 'click', function() { QR.open(); @@ -23955,9 +23879,7 @@ QR = (function() { linkBot = $.el('div', { className: "brackets-wrap qr-link-container-bottom" }); - $.extend(linkBot, { - innerHTML: "Reply to Thread" - }); + $.extend(linkBot, {innerHTML: "Reply to Thread"}); $.on(linkBot.firstElementChild, 'click', function() { QR.open(); return QR.nodes.com.focus(); @@ -24156,9 +24078,7 @@ QR = (function() { } }, connectionError: function() { - return $.el('span', { - innerHTML: "Connection error while posting. [More info]" - }); + return $.el('span', {innerHTML: "Connection error while posting. [More info]"}); }, notifications: [], cleanNotifications: function() { @@ -24306,9 +24226,7 @@ QR = (function() { openError: function() { var div; div = $.el('div'); - $.extend(div, { - innerHTML: "Could not open file. [More info]" - }); + $.extend(div, {innerHTML: "Could not open file. [More info]"}); return QR.error(div); }, setFile: function(e) { @@ -24486,9 +24404,7 @@ QR = (function() { dialog: function() { var classList, config, dialog, event, i, items, name, node, nodes, save, setNode; QR.nodes = nodes = { - el: dialog = UI.dialog('qr', { - innerHTML: "
×
No selected file
" - }) + el: dialog = UI.dialog('qr', {innerHTML: "
×
No selected file
"}) }; setNode = function(name, query) { return nodes[name] = $(query, dialog); @@ -24544,6 +24460,7 @@ QR = (function() { QR.flagsInput(); $.on(nodes.autohide, 'change', QR.toggleHide); $.on(nodes.close, 'click', QR.close); + $.on(nodes.status, 'click', QR.submit); $.on(nodes.form, 'submit', QR.submit); $.on(nodes.sjisToggle, 'click', QR.toggleSJIS); $.on(nodes.texButton, 'mousedown', QR.texPreviewShow); @@ -24657,19 +24574,24 @@ QR = (function() { } }, submit: function(e) { - var captcha, cb, err, filetag, formData, options, post, ref, thread, threadID; + var captcha, cb, err, filetag, force, formData, options, post, ref, thread, threadID; if (e != null) { e.preventDefault(); } + force = e != null ? e.shiftKey : void 0; if (QR.req) { QR.abort(); return; } $.forceSync('cooldowns'); if (QR.cooldown.seconds) { - QR.cooldown.auto = !QR.cooldown.auto; - QR.status(); - return; + if (force) { + QR.cooldown.clear(); + } else { + QR.cooldown.auto = !QR.cooldown.auto; + QR.status(); + return; + } } post = QR.posts[0]; post.forceSave(); @@ -24703,7 +24625,7 @@ QR = (function() { } } QR.cleanNotifications(); - if (err) { + if (err && !force) { QR.cooldown.auto = false; QR.status(); QR.error(err); @@ -24789,7 +24711,7 @@ QR = (function() { return QR.status(); }, response: function() { - var URL, _, connErr, err, h1, isReply, lastPostToThread, m, open, post, postID, postsCount, ref, ref1, ref2, ref3, seconds, threadID; + var URL, _, connErr, err, h1, isReply, j, lastPostToThread, len, m, mi, open, post, postID, postsCount, ref, ref1, ref2, ref3, seconds, threadID; if (this !== QR.req) { return; } @@ -24823,9 +24745,13 @@ QR = (function() { QR.cooldown.auto = QR.captcha.isEnabled || connErr; QR.cooldown.addDelay(post, 2); } - } else if (err.textContent && (m = err.textContent.match(/(?:(\d+)\s+minutes?\s+)?(\d+)\s+second/i)) && !/duplicate|hour/i.test(err.textContent)) { + } else if (err.textContent && (m = err.textContent.match(/\d+\s+(?:minute|second)/gi)) && !/duplicate|hour/i.test(err.textContent)) { QR.cooldown.auto = !/have\s+been\s+muted/i.test(err.textContent); - seconds = 60 * (+(m[1] || 0)) + (+m[2]); + seconds = 0; + for (j = 0, len = m.length; j < len; j++) { + mi = m[j]; + seconds += (/minute/i.test(mi) ? 60 : 1) * (+mi.match(/\d+/)[0]); + } if (/muted/i.test(err.textContent)) { QR.cooldown.addMute(seconds); } else { @@ -24858,10 +24784,10 @@ QR = (function() { postsCount = QR.posts.length - 1; QR.cooldown.auto = postsCount && isReply; lastPostToThread = !((function() { - var j, len, p, ref4; + var k, len1, p, ref4; ref4 = QR.posts.slice(1); - for (j = 0, len = ref4.length; j < len; j++) { - p = ref4[j]; + for (k = 0, len1 = ref4.length; k < len1; k++) { + p = ref4[k]; if (p.thread === post.thread) { return true; } @@ -25102,6 +25028,13 @@ QR = (function() { }); }); }, + clear: function() { + QR.cooldown.data = $.dict(); + QR.cooldown.changes = $.dict(); + QR.cooldown.auto = false; + QR.cooldown.update(); + return $.queueTask($["delete"], 'cooldowns'); + }, update: function() { var base, cooldown, cooldowns, elapsed, i, len, maxDelay, nCooldowns, now, ref, ref1, save, scope, seconds, start, suffix, threadID, type, update; if (!QR.cooldown.isCounting) { @@ -25524,9 +25457,7 @@ QR = (function() { draggable: true, href: 'javascript:;' }); - $.extend(el, { - innerHTML: "" - }); + $.extend(el, {innerHTML: ""}); this.nodes = { el: el, rm: el.firstChild, @@ -25763,9 +25694,7 @@ QR = (function() { div = $.el('div', { className: className }); - $.extend(div, { - innerHTML: E(message) + ((link) ? " [More info]" : "") + "
[delete post] [delete all]" - }); + $.extend(div, {innerHTML: E(message) + ((link) ? " [More info]" : "") + "
[delete post] [delete all]"}); (this.errors || (this.errors = [])).push(div); ref = $$('a', div), rm = ref[0], rmAll = ref[1]; $.on(div, 'click', (function(_this) { @@ -26568,16 +26497,12 @@ QuoteThreading = if (!(Conf['Quote Threading'] && g.VIEW === 'thread')) { return; } - this.controls = $.el('label', { - innerHTML: " Threading" - }); + this.controls = $.el('label', {innerHTML: " Threading"}); this.threadNewLink = $.el('span', { className: 'brackets-wrap threadnewlink', hidden: true }); - $.extend(this.threadNewLink, { - innerHTML: "Thread New Posts" - }); + $.extend(this.threadNewLink, {innerHTML: "Thread New Posts"}); this.input = $('input', this.controls); this.input.checked = Conf['Thread Quotes']; $.on(this.input, 'change', this.setEnabled); @@ -26872,9 +26797,7 @@ QuoteYou = (function() { var input, label, ref; label = $.el('label', { className: 'toggle-you' - }, { - innerHTML: " You" - }); + }, {innerHTML: " You"}); input = $('input', label); $.on(input, 'change', QuoteYou.menu.toggle); return (ref = Menu.menu) != null ? ref.addEntry({ @@ -27276,6 +27199,7 @@ Main = (function() { if ($.cantSet) { } else if (items.previousversion == null) { + Main.isFirstRun = true; Main.ready(function() { $.set('previousversion', g.VERSION); return Settings.open(); @@ -27299,9 +27223,7 @@ Main = (function() { return $.set(changes, function() { var el, ref; if ((ref = items['Show Updated Notifications']) != null ? ref : true) { - el = $.el('span', { - innerHTML: "4chan X has been updated to version " + E(g.VERSION) + "." - }); + el = $.el('span', {innerHTML: "4chan X has been updated to version " + E(g.VERSION) + "."}); return new Notice('info', el, 15); } }); @@ -27541,9 +27463,7 @@ Main = (function() { return; } if (typeof (base1 = g.SITE).isIncomplete === "function" ? base1.isIncomplete() : void 0) { - msg = $.el('div', { - innerHTML: "The page didn't load completely.
Some features may not work unless you reload." - }); + msg = $.el('div', {innerHTML: "The page didn't load completely.
Some features may not work unless you reload."}); $.on($('a', msg), 'click', function() { return location.reload(); }); @@ -27627,7 +27547,8 @@ Main = (function() { err = error1; errors.push({ message: "Parsing of Post No." + (postRoot.id.match(/\d+/)) + " failed. Post will be skipped.", - error: err + error: err, + html: postRoot.outerHTML }); } } @@ -27746,7 +27667,8 @@ Main = (function() { err = error1; errors.push({ message: "Parsing of Catalog Thread No." + ((threadRoot.dataset.id || threadRoot.id).match(/\d+/)) + " failed. Thread will be skipped.", - error: err + error: err, + html: threadRoot.outerHTML }); } } @@ -27810,11 +27732,21 @@ Main = (function() { return softTask(); }, handleErrors: function(errors) { - var div, error, j, len, logs; + var div, enabled, error, j, len, logs, msg; if (d.body && $.hasClass(d.body, 'fourchan_x') && !$.hasClass(doc, 'tainted')) { new Notice('error', 'Error: Multiple copies of 4chan X are enabled.'); $.addClass(doc, 'tainted'); } + if (g.SITE.testNativeExtension && !$.hasClass(doc, 'tainted')) { + enabled = g.SITE.testNativeExtension().enabled; + if (enabled) { + $.addClass(doc, 'tainted'); + if (Conf['Disable Native Extension'] && !Main.isFirstRun) { + msg = $.el('div', {innerHTML: "Failed to disable the native extension. You may need to block it."}); + new Notice('error', msg); + } + } + } if (!(errors instanceof Array)) { error = errors; } else if (errors.length === 1) { @@ -27824,9 +27756,7 @@ Main = (function() { new Notice('error', Main.parseError(error, Main.reportLink([error])), 15); return; } - div = $.el('div', { - innerHTML: E(errors.length) + " errors occurred." + (Main.reportLink(errors)).innerHTML + " [show]" - }); + div = $.el('div', {innerHTML: E(errors.length) + " errors occurred." + (Main.reportLink(errors)).innerHTML + " [show]"}); $.on(div.lastElementChild, 'click', function() { var ref; return ref = this.textContent === 'show' ? ['hide', false] : ['show', true], this.textContent = ref[0], logs.hidden = ref[1], ref; @@ -27843,9 +27773,7 @@ Main = (function() { parseError: function(data, reportLink) { var context, error, lines, message, ref, ref1; c.error(data.message, data.error.stack); - message = $.el('div', { - innerHTML: E(data.message) + ((reportLink) ? (reportLink).innerHTML : "") - }); + message = $.el('div', {innerHTML: E(data.message) + ((reportLink) ? (reportLink).innerHTML : "")}); error = $.el('div', { textContent: (data.error.name || 'Error') + ": " + (data.error.message || 'see console for details') }); @@ -27856,7 +27784,7 @@ Main = (function() { return [message, error, context]; }, reportLink: function(errors) { - var addDetails, data, details, title, url; + var addDetails, data, details, info, title, url; data = errors[0]; title = data.message; if (errors.length > 1) { @@ -27868,7 +27796,10 @@ Main = (function() { return details += text + '\n'; } }; - addDetails("[Please describe the steps needed to reproduce this error.]\n\nScript: 4chan X ccd0 v" + g.VERSION + " " + $.platform + "\nUser agent: " + navigator.userAgent + "\nURL: " + location.href); + addDetails("[Please describe the steps needed to reproduce this error.]\n\nScript: 4chan X ccd0 v" + g.VERSION + " " + $.platform + "\nURL: " + location.href + "\nUser agent: " + navigator.userAgent); + if ($.platform === 'userscript' && (info = typeof GM !== "undefined" && GM !== null ? GM.info : (typeof GM_info !== "undefined" && GM_info !== null ? GM_info : void 0))) { + addDetails("Userscript manager: " + info.scriptHandler + " " + info.version); + } addDetails('\n' + data.error); if (data.error.stack) { addDetails(data.error.stack.replace(data.error.toString(), '').trim()); @@ -27877,10 +27808,8 @@ Main = (function() { addDetails('\n`' + data.html + '`'); } details = details.replace(/file:\/{3}.+\//g, ''); - url = "https://gitreports.com/issue/ccd0/4chan-x?issue_title=" + (encodeURIComponent(title)) + "&details=" + (encodeURIComponent(details)); - return { - innerHTML: " [report]" - }; + url = 'https://gitreports.com/issue/ccd0/4chan-x?issue_title=%title&details=%details'.replace('%title', encodeURIComponent(title)).replace('%details', encodeURIComponent(details)); + return {innerHTML: " [report]"}; }, isThisPageLegit: function() { if (!('thisPageIsLegit' in Main)) { diff --git a/builds/4chan-X.crx b/builds/4chan-X.crx index 28a046201..0bb29dedd 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 dd50574b9..5f76c94b5 100644 --- a/builds/4chan-X.meta.js +++ b/builds/4chan-X.meta.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.14.11.1 +// @version 1.14.12.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 5b6e4f0fb..84faca6aa 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name 4chan X -// @version 1.14.11.1 +// @version 1.14.12.0 // @minGMVer 1.14 // @minFFVer 26 // @namespace 4chan-X @@ -199,7 +199,7 @@ docSet = function() { }; g = { - VERSION: '1.14.11.1', + VERSION: '1.14.12.0', NAMESPACE: '4chan X.', sites: Object.create(null), boards: Object.create(null) @@ -575,7 +575,8 @@ Config = (function() { ], fourchanImageHost: 'i.4cdn.org', hiddenPSAList: [{}], - 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' + 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: [[]] }; return Config; @@ -2017,8 +2018,8 @@ div[data-checked=\"false\"] > .suboption-list {\n\ }\n\ #fourchanx-settings textarea {\n\ font-family: monospace;\n\ - min-width: 100%;\n\ - max-width: 100%;\n\ + width: 100%;\n\ + resize: vertical;\n\ }\n\ #fourchanx-settings code {\n\ color: #000;\n\ @@ -5247,23 +5248,23 @@ $ = (function() { } })(); - $.globalEval = function(code, data) { - var script; - script = $.el('script', { - textContent: code - }); - if (data) { - $.extend(script.dataset, data); - } - $.add(d.head || doc, script); - return $.rm(script); - }; - $.global = function(fn, data) { + var script; if (doc) { - return $.globalEval("(" + fn + ")();", data); + script = $.el('script', { + textContent: "(" + fn + ").call(document.currentScript.dataset);" + }); + if (data) { + $.extend(script.dataset, data); + } + $.add(d.head || doc, script); + $.rm(script); + return script.dataset; } else { - return fn(); + try { + fn.call(data); + } catch (error) {} + return data; } }; @@ -6627,23 +6628,17 @@ Fetcher = (function() { results1 = []; for (j = l = 0, len1 = ref.length; l < len1; j = ++l) { text2 = ref[j]; - results1.push({ - innerHTML: ((j % 2) ? "" + E(text2) + "" : E(text2)) - }); + results1.push({innerHTML: ((j % 2) ? "" + E(text2) + "" : E(text2))}); } return results1; })(); - text = { - innerHTML: ((greentext) ? "" + E.cat(text) + "" : E.cat(text)) - }; + text = {innerHTML: ((greentext) ? "" + E.cat(text) + "" : E.cat(text))}; results.push(text); } } return results; }).call(this); - comment = { - innerHTML: E.cat(comment) - }; + comment = {innerHTML: E.cat(comment)}; this.threadID = +data.thread_num; o = { ID: this.postID, @@ -6734,71 +6729,29 @@ Fetcher = (function() { }; Fetcher.prototype.archiveTags = { - '\n': { - innerHTML: "
" - }, - '[b]': { - innerHTML: "" - }, - '[/b]': { - innerHTML: "" - }, - '[spoiler]': { - innerHTML: "" - }, - '[/spoiler]': { - innerHTML: "" - }, - '[code]': { - innerHTML: "
"
-      },
-      '[/code]': {
-        innerHTML: "
" - }, - '[moot]': { - innerHTML: "
" - }, - '[/moot]': { - innerHTML: "
" - }, - '[banned]': { - innerHTML: "" - }, - '[/banned]': { - innerHTML: "" - }, + '\n': {innerHTML: "
"}, + '[b]': {innerHTML: ""}, + '[/b]': {innerHTML: ""}, + '[spoiler]': {innerHTML: ""}, + '[/spoiler]': {innerHTML: ""}, + '[code]': {innerHTML: "
"},
+      '[/code]': {innerHTML: "
"}, + '[moot]': {innerHTML: "
"}, + '[/moot]': {innerHTML: "
"}, + '[banned]': {innerHTML: ""}, + '[/banned]': {innerHTML: ""}, '[fortune]': function(text) { - return { - innerHTML: "" - }; + return {innerHTML: ""}; }, - '[/fortune]': { - innerHTML: "" - }, - '[i]': { - innerHTML: "" - }, - '[/i]': { - innerHTML: "" - }, - '[red]': { - innerHTML: "" - }, - '[/red]': { - innerHTML: "" - }, - '[green]': { - innerHTML: "" - }, - '[/green]': { - innerHTML: "" - }, - '[blue]': { - innerHTML: "" - }, - '[/blue]': { - innerHTML: "" - } + '[/fortune]': {innerHTML: ""}, + '[i]': {innerHTML: ""}, + '[/i]': {innerHTML: ""}, + '[red]': {innerHTML: ""}, + '[/red]': {innerHTML: ""}, + '[green]': {innerHTML: ""}, + '[/green]': {innerHTML: ""}, + '[blue]': {innerHTML: ""}, + '[/blue]': {innerHTML: ""} }; return Fetcher; @@ -6819,9 +6772,7 @@ Notice = (function() { this.onclose = onclose; this.close = bind(this.close, this); this.add = bind(this.add, this); - this.el = $.el('div', { - innerHTML: "
" - }); + this.el = $.el('div', {innerHTML: "
"}); this.el.style.opacity = 0; this.setType(type); $.on(this.el.firstElementChild, 'click', this.close); @@ -7115,8 +7066,8 @@ Post = (function() { } $.extend(file, { url: file.link.href, - isImage: /(jpe?g|png|gif|bmp)$/i.test(file.link.href), - isVideo: /(webm|mp4)$/i.test(file.link.href) + isImage: /\.(jpe?g|png|gif|bmp)$/i.test(file.link.href), + isVideo: /\.(webm|mp4)$/i.test(file.link.href) }); size = +file.size.match(/[\d.]+/)[0]; unit = ['B', 'KB', 'MB', 'GB'].indexOf(file.size.match(/\w+$/)[0]); @@ -8329,6 +8280,13 @@ SW = {}; }, isLinkified: function(link) { return ImageHost.test(link.hostname); + }, + testNativeExtension: function() { + return $.global(function() { + if (window.Parser.postMenuIcon) { + return this.enabled = 'true'; + } + }); } }; @@ -8508,9 +8466,7 @@ SW = {}; url = Build.threadURL(boardID, threadID); postLink = url + "#p" + ID; quoteLink = Build.sameThread(boardID, threadID) ? "javascript:quote('" + (+ID) + "');" : url + "#q" + ID; - postInfo = { - innerHTML: "
" + ((!o.isReply || boardID === "f" || subject) ? "" + E(subject || "") + " " : "") + "" + ((email) ? "" : "") + "" + E(name) + "" + ((tripcode) ? " " + E(tripcode) + "" : "") + ((o.extra.xa19s) ? " " + E(o.extra.xa19s) + "" : "") + ((pass) ? " " : "") + ((capcode) ? " ## " + E(capcode) + "" : "") + ((email) ? "" : "") + ((boardID === "f" && !o.isReply || capcodeDescription) ? "" : " ") + ((capcodeDescription) ? " \""" : "") + ((uniqueID && !capcode) ? " (ID: " + E(uniqueID) + ")" : "") + ((flagCode) ? " " : "") + ((flagCodeTroll) ? " \""" : "") + " " + E(dateText) + " No." + E(ID) + "" + ((o.extra.xa19l && o.isReply) ? " Like! ×" + E(o.extra.xa19l) + "" : "") + ((o.isSticky) ? " \"Sticky\"" : "") + ((o.isClosed && !o.isArchived) ? " \"Closed\"" : "") + ((o.isArchived) ? " \"Archived\"" : "") + ((!o.isReply && g.VIEW === "index") ? "   [Reply]" : "") + "
" - }; + postInfo = {innerHTML: "
" + ((!o.isReply || boardID === "f" || subject) ? "" + E(subject || "") + " " : "") + "" + ((email) ? "" : "") + "" + E(name) + "" + ((tripcode) ? " " + E(tripcode) + "" : "") + ((o.extra.xa19s) ? " " + E(o.extra.xa19s) + "" : "") + ((pass) ? " " : "") + ((capcode) ? " ## " + E(capcode) + "" : "") + ((email) ? "" : "") + ((boardID === "f" && !o.isReply || capcodeDescription) ? "" : " ") + ((capcodeDescription) ? " \""" : "") + ((uniqueID && !capcode) ? " (ID: " + E(uniqueID) + ")" : "") + ((flagCode) ? " " : "") + ((flagCodeTroll) ? " \""" : "") + " " + E(dateText) + " No." + E(ID) + "" + ((o.extra.xa19l && o.isReply) ? " Like! ×" + E(o.extra.xa19l) + "" : "") + ((o.isSticky) ? " \"Sticky\"" : "") + ((o.isClosed && !o.isArchived) ? " \"Closed\"" : "") + ((o.isArchived) ? " \"Archived\"" : "") + ((!o.isReply && g.VIEW === "index") ? "   [Reply]" : "") + "
"}; /* File Info */ if (file) { @@ -8519,15 +8475,11 @@ SW = {}; shortFilename = Build.shortFilename(file.name); fileThumb = file.isSpoiler ? Build.spoilerThumb(boardID) : file.thumbURL.replace(protocol, ''); } - fileBlock = { - innerHTML: ((file) ? "
" + ((boardID === "f") ? "
File: " + E(file.name) + "-(" + E(file.size) + ", " + E(file.dimensions) + ((file.tag) ? ", " + E(file.tag) : "") + ")
" : "
File: " + ((file.isSpoiler) ? "Spoiler Image" : E(shortFilename)) + " (" + E(file.size) + ", " + E(file.dimensions || "PDF") + ")
\""") + "
" : ((o.fileDeleted) ? "
\"File
" : "")) - }; + fileBlock = {innerHTML: ((file) ? "
" + ((boardID === "f") ? "
File: " + E(file.name) + "-(" + E(file.size) + ", " + E(file.dimensions) + ((file.tag) ? ", " + E(file.tag) : "") + ")
" : "
File: " + ((file.isSpoiler) ? "Spoiler Image" : E(shortFilename)) + " (" + E(file.size) + ", " + E(file.dimensions || "PDF") + ")
\""") + "
" : ((o.fileDeleted) ? "
\"File
" : ""))}; /* Whole Post */ postClass = o.isReply ? 'reply' : 'op'; - wholePost = { - innerHTML: ((o.isReply) ? "
>>
" : "") + "
" + ((o.isReply) ? (postInfo).innerHTML + (fileBlock).innerHTML : (fileBlock).innerHTML + (postInfo).innerHTML) + "
" + (commentHTML).innerHTML + "
" - }; + wholePost = {innerHTML: ((o.isReply) ? "
>>
" : "") + "
" + ((o.isReply) ? (postInfo).innerHTML + (fileBlock).innerHTML : (fileBlock).innerHTML + (postInfo).innerHTML) + "
" + (commentHTML).innerHTML + "
"}; container = $.el('div', { className: "postContainer " + postClass + "Container", id: "pc" + ID @@ -8618,9 +8570,7 @@ SW = {}; } postCount = data.replies + 1; fileCount = data.images + !!data.ext; - container = $.el('div', { - innerHTML: "
" + E(postCount) + " / " + E(fileCount) + " / " + E(pageCount) + "" + ((thread.isSticky) ? "" : "") + ((thread.isClosed) ? "" : "") + "
" - }); + container = $.el('div', {innerHTML: "
" + E(postCount) + " / " + E(fileCount) + " / " + E(pageCount) + "" + ((thread.isSticky) ? "" : "") + ((thread.isClosed) ? "" : "") + "
"}); $.before(thread.OP.nodes.info, slice.call(container.childNodes)); ref = $$('br', thread.OP.nodes.comment); for (i = 0, len = ref.length; i < len; i++) { @@ -8661,9 +8611,7 @@ SW = {}; link = Build.postURL(thread.board.ID, thread.ID, data.no); return $.el('div', { className: 'catalog-reply' - }, { - innerHTML: ": " + E(excerpt) + "..." - }); + }, {innerHTML: ": " + E(excerpt) + "..."}); } }; @@ -9077,7 +9025,6 @@ Filter = (function() { Filter = { filters: $.dict(), - results: $.dict(), 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' || ref === 'catalog') && Conf['Filter'])) { @@ -9472,7 +9419,7 @@ Filter = (function() { return ta.focus(); }); }, - quickFilterMD5: function() { + quickFilterMD5: function(e) { var files, filter, links, msg, notice, origin, post; post = Get.postFromNode(this); files = post.files.filter(function(f) { @@ -9491,15 +9438,19 @@ Filter = (function() { } else if (g.VIEW === 'index') { ThreadHiding.hide(origin.thread); } + if (e.type !== 'keydown') { + if (post.nodes.post.getBoundingClientRect().height) { + new Notice('info', 'MD5 filtered.', 2); + } + return; + } notice = Filter.quickFilterMD5.notice; if (notice) { notice.filters.push(filter); notice.posts.push(origin); return $('span', notice.el).textContent = notice.filters.length + " MD5s filtered."; } else { - msg = $.el('div', { - innerHTML: "MD5 filtered. [show] [undo]" - }); + msg = $.el('div', {innerHTML: "MD5 filtered. [show] [undo]"}); notice = Filter.quickFilterMD5.notice = new Notice('info', msg, void 0, function() { return delete Filter.quickFilterMD5.notice; }); @@ -10204,9 +10155,7 @@ ThreadHiding = (function() { className: type + "-thread-button", href: 'javascript:;' }); - $.extend(a, { - innerHTML: "" - }); + $.extend(a, {innerHTML: ""}); a.dataset.fullID = thread.fullID; $.on(a, 'click', ThreadHiding.toggle); return a; @@ -10524,13 +10473,22 @@ Header = (function() { Header = { init: function() { var barFixedToggler, barPositionToggler, box, cs, customNavToggler, editCustomNav, footerToggler, headerToggler, linkJustifyToggler, menuButton, scrollHeaderToggler, shortcutToggler; + $.onExists(doc, 'body', (function(_this) { + return function() { + if (!Main.isThisPageLegit()) { + return; + } + $.add(_this.bar, [_this.noticesRoot, _this.toggle]); + $.prepend(d.body, _this.bar); + $.add(d.body, Header.hover); + return _this.setBarPosition(Conf['Bottom Header']); + }; + })(this)); this.menu = new UI.Menu('header'); menuButton = $.el('span', { className: 'menu-button' }); - $.extend(menuButton, { - innerHTML: "" - }); + $.extend(menuButton, {innerHTML: ""}); box = UI.checkbox; barFixedToggler = box('Fixed Header', 'Fixed Header'); headerToggler = box('Header auto-hide', 'Auto-hide header'); @@ -10606,16 +10564,6 @@ Header = (function() { $.on(window, 'load popstate', Header.hashScroll); $.on(d, 'CreateNotification', this.createNotification); this.setBoardList(); - $.onExists(doc, 'body', (function(_this) { - return function() { - if (!Main.isThisPageLegit()) { - return; - } - $.prepend(d.body, _this.bar); - $.add(d.body, Header.hover); - return _this.setBarPosition(Conf['Bottom Header']); - }; - })(this)); $.onExists(doc, g.SITE.selectors.boardList + " + *", Header.generateFullBoardList); Main.ready(function() { var a, absbot, footer, j, len, ref; @@ -10628,7 +10576,9 @@ Header = (function() { $('#navtopright', footer).id = 'navbotright'; $('#settingsWindowLink', footer).id = 'settingsWindowLinkBot'; $.before(absbot, footer); - $.globalEval('window.cloneTopNav = function() {};'); + $.global(function() { + return window.cloneTopNav = function() {}; + }); } if ((Header.bottomBoardList = $(g.SITE.selectors.boardListBottom))) { ref = $$('a', Header.bottomBoardList); @@ -10679,12 +10629,10 @@ Header = (function() { Header.boardList = boardList = $.el('span', { id: 'board-list' }); - $.extend(boardList, { - innerHTML: "" - }); + $.extend(boardList, {innerHTML: ""}); btn = $('.hide-board-list-button', boardList); $.on(btn, 'click', Header.toggleBoardList); - $.add(Header.bar, [Header.boardList, Header.shortcuts, Header.noticesRoot, Header.toggle]); + $.prepend(Header.bar, [Header.boardList, Header.shortcuts]); Header.setCustomNav(Conf['Custom Board Navigation']); Header.generateBoardList(Conf['boardnav']); $.sync('Custom Board Navigation', Header.setCustomNav); @@ -10971,8 +10919,10 @@ Header = (function() { return Header.previousOffset = offsetY; }, setBarPosition: function(bottom) { - var args; - Header.barPositionToggler.checked = bottom; + var args, ref; + if ((ref = Header.barPositionToggler) != null) { + ref.checked = bottom; + } $.event('CloseMenu'); args = bottom ? ['bottom-header', 'top-header', 'after'] : ['top-header', 'bottom-header', 'add']; $.addClass(doc, args[0]); @@ -11156,9 +11106,7 @@ Header = (function() { case 'denied': return; } - el = $.el('span', { - innerHTML: "4chan X needs your permission to show desktop notifications. [FAQ]
or " - }); + el = $.el('span', {innerHTML: "4chan X needs your permission to show desktop notifications. [FAQ]
or "}); ref = $$('button', el), authorize = ref[0], disable = ref[1]; $.on(authorize, 'click', function() { return Notification.requestPermission(function(status) { @@ -11281,9 +11229,7 @@ Index = (function() { this.navLinks = $.el('div', { className: 'navLinks json-index' }); - $.extend(this.navLinks, { - innerHTML: "Index Catalog Archive Bottom ×" - }); + $.extend(this.navLinks, {innerHTML: "Index Catalog Archive Bottom ×"}); $('.cataloglink a', this.navLinks).href = CatalogLinks.catalog(); if (!BoardConfig.isArchived(g.BOARD.ID)) { $('.archlistlink', this.navLinks).hidden = true; @@ -11331,9 +11277,7 @@ Index = (function() { this.pagelist = $.el('div', { className: 'pagelist json-index' }); - $.extend(this.pagelist, { - innerHTML: "
" - }); + $.extend(this.pagelist, {innerHTML: "
"}); $('.cataloglink a', this.pagelist).href = CatalogLinks.catalog(); $.on(this.pagelist, 'click', this.cb.pageNav); this.update(true); @@ -11421,9 +11365,7 @@ Index = (function() { el: $.el('a', { href: 'javascript:;', className: 'has-shortcut-text' - }, { - innerHTML: "Shift+click" - }), + }, {innerHTML: "Shift+click"}), order: 20, open: function(arg) { var thread; @@ -12084,7 +12026,7 @@ Index = (function() { return PostHiding.isHidden(g.BOARD.ID, threadID, replyData.no) || Filter.isHidden(g.SITE.Build.parseJSON(replyData, g.BOARD)); }, buildThreads: function(threadIDs, isCatalog, withReplies) { - var ID, OP, err, errors, isStale, k, lastPost, len1, newPosts, newThreads, obj, t, thread, threadData, threads; + var ID, OP, err, errors, isStale, k, lastPost, len1, newPosts, newThreads, obj, opRoot, t, thread, threadData, threads; threads = []; newThreads = []; newPosts = []; @@ -12119,7 +12061,8 @@ Index = (function() { thread.setPage(Math.floor(Index.threadPosition[ID] / Index.threadsNumPerPage) + 1); } else { obj = Index.parsedThreads[ID]; - OP = new Post(g.SITE.Build.post(obj), thread, g.BOARD); + opRoot = g.SITE.Build.post(obj); + OP = new Post(opRoot, thread, g.BOARD); OP.filterResults = obj.filterResults; newPosts.push(OP); } @@ -12133,7 +12076,8 @@ Index = (function() { } errors.push({ message: "Parsing of Thread No." + thread + " failed. Thread will be skipped.", - error: err + error: err, + html: opRoot != null ? opRoot.outerHTML : void 0 }); } } @@ -12185,7 +12129,8 @@ Index = (function() { } errors.push({ message: "Parsing of Post No." + data.no + " failed. Post will be skipped.", - error: err + error: err, + html: node != null ? node.outerHTML : void 0 }); } } @@ -12570,7 +12515,7 @@ Settings = (function() { $.on(d, 'OpenSettings', function(e) { return Settings.open(e.detail); }); - if (Conf['Disable Native Extension']) { + if (g.SITE.software === 'yotsuba' && Conf['Disable Native Extension']) { if ($.hasStorage) { return $.global(function() { var settings; @@ -12608,9 +12553,7 @@ Settings = (function() { $.event('CloseMenu'); Settings.dialog = dialog = $.el('div', { id: 'overlay' - }, { - innerHTML: "" - }); + }, {innerHTML: ""}); $.on($('.export', dialog), 'click', Settings["export"]); $.on($('.import', dialog), 'click', Settings["import"]); $.on($('.reset', dialog), 'click', Settings.reset); @@ -12699,9 +12642,7 @@ Settings = (function() { boardID: 'qa', threadID: 362590 }); - return cb($.el('li', { - innerHTML: "To protect yourself from malicious ads, you should block ads on 4chan." - })); + return cb($.el('li', {innerHTML: "To protect yourself from malicious ads, you should block ads on 4chan."})); }); }); } @@ -12710,9 +12651,7 @@ Settings = (function() { var addCheckboxes, addWarning, button, div, fs, inputs, items, key, keyFS, obj, ref, ref1, warning, warnings; warnings = $.el('fieldset', { hidden: true - }, { - innerHTML: "Warnings
    " - }); + }, {innerHTML: "Warnings
      "}); addWarning = function(item) { $.add($('ul', warnings), item); return warnings.hidden = false; @@ -12735,9 +12674,7 @@ Settings = (function() { continue; } description = arr[1]; - div = $.el('div', { - innerHTML: ": " + E(description) + "" - }); + div = $.el('div', {innerHTML: ": " + E(description) + ""}); div.dataset.name = key; input = $('input', div); $.on(input, 'change', $.cb.checked); @@ -12763,14 +12700,10 @@ Settings = (function() { ref1 = Config.main; for (keyFS in ref1) { obj = ref1[keyFS]; - fs = $.el('fieldset', { - innerHTML: "" + E(keyFS) + "" - }); + fs = $.el('fieldset', {innerHTML: "" + E(keyFS) + ""}); addCheckboxes(fs, obj); if (keyFS === 'Posting and Captchas') { - $.add(fs, $.el('p', { - innerHTML: "For more info on captcha options and issues, see the captcha FAQ." - })); + $.add(fs, $.el('p', {innerHTML: "For more info on captcha options and issues, see the captcha FAQ."})); } $.add(section, fs); } @@ -12789,9 +12722,7 @@ Settings = (function() { inputs[key].parentNode.parentNode.dataset.checked = val; } }); - div = $.el('div', { - innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." - }); + div = $.el('div', {innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply."}); button = $('button', div); $.get({ hiddenThreads: $.dict(), @@ -12935,17 +12866,20 @@ Settings = (function() { 'Disable 4chan\'s extension': 'Disable Native Extension', 'Comment Auto-Expansion': '', 'Remove Slug': '', + 'Always HTTPS': 'Redirect to HTTPS', 'Check for Updates': '', 'Recursive Filtering': 'Recursive Hiding', 'Reply Hiding': 'Reply Hiding Buttons', 'Thread Hiding': 'Thread Hiding Buttons', 'Show Stubs': 'Stubs', 'Image Auto-Gif': 'Replace GIF', + 'Expand All WebM': 'Expand videos', 'Reveal Spoilers': 'Reveal Spoiler Thumbnails', 'Expand From Current': 'Expand from here', 'Current Page': 'Page Count in Stats', 'Current Page Position': '', 'Alternative captcha': 'Use Recaptcha v1', + 'Alt index captcha': 'Use Recaptcha v1 on Index', 'Auto Submit': 'Post on Captcha Completion', 'Open Reply in New Tab': 'Open Post in New Tab', 'Remember QR size': 'Remember QR Size', @@ -12967,6 +12901,7 @@ Settings = (function() { 'spoiler': 'Spoiler tags', 'sageru': 'Toggle sage', 'code': 'Code tags', + 'sjis': 'SJIS tags', 'submit': 'Submit QR', 'watch': 'Watch', 'update': 'Update', @@ -12987,6 +12922,10 @@ Settings = (function() { 'Scrolling': 'Auto Scroll', 'Verbose': '' }); + if ('Always CDN' in data.Conf) { + data.Conf['fourchanImageHost'] = data.Conf['Always CDN'] ? 'i.4cdn.org' : ''; + delete data.Conf['Always CDN']; + } data.Conf.sauces = data.Conf.sauces.replace(/\$\d/g, function(c) { switch (c) { case '$1': @@ -13013,15 +12952,17 @@ Settings = (function() { } } if (data.WatchedThreads) { - data.Conf['watchedThreads'] = { - boards: $.dict() - }; + data.Conf['watchedThreads'] = $.dict.clone({ + '4chan.org': { + boards: {} + } + }); ref1 = data.WatchedThreads; for (boardID in ref1) { threads = ref1[boardID]; for (threadID in threads) { threadData = threads[threadID]; - ((base = data.Conf['watchedThreads'].boards)[boardID] || (base[boardID] = $.dict()))[threadID] = { + ((base = data.Conf['watchedThreads']['4chan.org'].boards)[boardID] || (base[boardID] = $.dict()))[threadID] = { excerpt: threadData.textContent }; } @@ -13361,9 +13302,7 @@ Settings = (function() { }, filter: function(section) { var select; - $.extend(section, { - innerHTML: "
      " - }); + $.extend(section, {innerHTML: "
      "}); select = $('select', section); $.on(select, 'change', Settings.selectFilter); return Settings.selectFilter.call(select); @@ -13391,20 +13330,14 @@ Settings = (function() { filterTypes = Object.keys(Config.filter).filter(function(x) { return x !== 'general'; }).map(function(x, i) { - return { - innerHTML: ((i) ? "," : "") + "" + E(x) - }; - }); - $.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;.
      " + return {innerHTML: ((i) ? "," : "") + "" + E(x)}; }); + $.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;.
      "}); return $('.warning', div).hidden = Conf['Filter']; }, sauce: function(section) { var ta; - $.extend(section, { - innerHTML: "
      Sauce is disabled.
      These parameters will be replaced by their corresponding values in the URL and displayed text:
      • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
      • %URL: Full image URL.
      • %TURL: Thumbnail URL.
      • %name: Original file name.
      • %board: Current board.
      • %MD5: MD5 hash in base64.
      • %sMD5: MD5 hash in base64 using - and _.
      • %hMD5: MD5 hash in hexadecimal.
      • %$0: Matched regular expression within the filename.
      • %$1, %$2, %$3, ... : Subexpressions within the matched regular expression.
      • %%, %semi: Literal % and ;.
      Lines starting with a # will be ignored.
      You can specify a display text by appending ;text:[text] to the URL.
      You can specify the applicable boards/sites by appending ;boards:[board1],[board2]. See the Filter guide for details.
      You can specify the applicable file types by appending ;types:[extension1],[extension2].
      You can specify a regular expression the filename must match by appending ;regexp:[regular expression].
      " - }); + $.extend(section, {innerHTML: "
      Sauce is disabled.
      These parameters will be replaced by their corresponding values in the URL and displayed text:
      • %IMG: Full image URL for GIF, JPG, and PNG; thumbnail URL for other types.
      • %URL: Full image URL.
      • %TURL: Thumbnail URL.
      • %name: Original file name.
      • %board: Current board.
      • %MD5: MD5 hash in base64.
      • %sMD5: MD5 hash in base64 using - and _.
      • %hMD5: MD5 hash in hexadecimal.
      • %$0: Matched regular expression within the filename.
      • %$1, %$2, %$3, ... : Subexpressions within the matched regular expression.
      • %%, %semi: Literal % and ;.
      Lines starting with a # will be ignored.
      You can specify a display text by appending ;text:[text] to the URL.
      You can specify the applicable boards/sites by appending ;boards:[board1],[board2]. See the Filter guide for details.
      You can specify the applicable file types by appending ;types:[extension1],[extension2].
      You can specify a regular expression the filename must match by appending ;regexp:[regular expression].
      "}); $('.warning', section).hidden = Conf['Sauce']; ta = $('textarea', section); $.get('sauces', Conf['sauces'], function(item) { @@ -13415,9 +13348,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"
      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.
      :
      Day: %a, %A, %d, %e
      Month: %m, %b, %B
      Year: %y, %Y
      Hour: %k, %H, %l, %I, %p, %P
      Minute: %M
      Second: %S
      Literal %: %%
      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.

        You can use these settings with each item, separate them with semicolons:
      • Possible items are: name, options (or equivalently email), subject and password.
      • Wrap values of items with quotes, like this: options:"sage".
      • Force values as defaults with the always keyword, for example: options:"sage";always.
      • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
      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:
      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"
      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.
      :
      Day: %a, %A, %d, %e
      Month: %m, %b, %B
      Year: %y, %Y
      Hour: %k, %H, %l, %I, %p, %P
      Minute: %M
      Second: %S
      Literal %: %%
      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.

        You can use these settings with each item, separate them with semicolons:
      • Possible items are: name, options (or equivalently email), subject and password.
      • Wrap values of items with quotes, like this: options:"sage".
      • Force values as defaults with the always keyword, for example: options:"sage";always.
      • Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always.
      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]; @@ -13607,9 +13538,7 @@ Settings = (function() { textContent: archive[1] })); } - $.extend(td, { - innerHTML: "" - }); + $.extend(td, {innerHTML: ""}); select = td.firstElementChild; if (!(select.disabled = length === 1)) { select.setAttribute('data-boardid', boardID); @@ -13731,9 +13660,7 @@ Settings = (function() { }, keybinds: function(section) { var arr, input, inputs, items, key, ref, tbody, tr; - $.extend(section, { - innerHTML: "
      Keybinds are disabled.
      Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
      Press Backspace to disable a keybind.
      ActionsKeybinds
      " - }); + $.extend(section, {innerHTML: "
      Keybinds are disabled.
      Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
      Press Backspace to disable a keybind.
      ActionsKeybinds
      "}); $('.warning', section).hidden = Conf['Keybinds']; tbody = $('tbody', section); items = $.dict(); @@ -13741,9 +13668,7 @@ Settings = (function() { ref = Config.hotkeys; for (key in ref) { arr = ref[key]; - tr = $.el('tr', { - innerHTML: "" + E(arr[1]) + "" - }); + tr = $.el('tr', {innerHTML: "" + E(arr[1]) + ""}); input = $('input', tr); input.name = key; input.spellcheck = false; @@ -14424,9 +14349,7 @@ Gallery = (function() { nodes.el = dialog = $.el('div', { id: 'a-gallery' }); - $.extend(dialog, { - innerHTML: "
      × /
      " - }); + $.extend(dialog, {innerHTML: "
      × /
      "}); ref = { buttons: '.gal-buttons', frame: '.gal-image', @@ -14875,9 +14798,7 @@ Gallery = (function() { } return results; })(); - delayLabel = $.el('label', { - innerHTML: "Slide Delay: " - }); + delayLabel = $.el('label', {innerHTML: "Slide Delay: "}); delayInput = delayLabel.firstElementChild; delayInput.value = Gallery.delay; $.on(delayInput, 'change', Gallery.cb.setDelay); @@ -14991,7 +14912,7 @@ ImageCommon = (function() { onloadend: function() { var i, len, postObj, ref, ref1; if (this.status === 404) { - post.kill(!post.isClone); + post.kill(!post.isClone, fileObj.index); } if (this.status !== 200) { return redirect(); @@ -15083,9 +15004,7 @@ ImageExpand = (function() { this.videoControls = $.el('span', { className: 'video-controls' }); - $.extend(this.videoControls, { - innerHTML: " contract" - }); + $.extend(this.videoControls, {innerHTML: " contract"}); return Callbacks.Post.push({ name: 'Image Expansion', cb: this.node @@ -15693,9 +15612,7 @@ ImageLoader = (function() { if (!(Conf['Image Prefetching'] && ((ref1 = g.VIEW) === 'index' || ref1 === 'thread'))) { return; } - prefetch = $.el('label', { - innerHTML: " Prefetch Images" - }); + prefetch = $.el('label', {innerHTML: " Prefetch Images"}); this.el = prefetch.firstElementChild; $.on(this.el, 'change', this.toggle); return Header.menu.addEntry({ @@ -15741,12 +15658,19 @@ ImageLoader = (function() { return file.videoThumb = true; }, prefetch: function(post, file) { - var clone, el, i, isImage, isVideo, len, match, ref, replace, thumb, type, url; + var clone, el, i, isImage, isVideo, len, ref, ref1, replace, thumb, type, url; isImage = file.isImage, isVideo = file.isVideo, thumb = file.thumb, url = file.url; if (file.isPrefetched || !(isImage || isVideo) || post.isHidden || post.thread.isHidden) { return; } - type = (match = url.match(/\.([^.]+)$/)[1].toUpperCase()) === 'JPEG' ? 'JPG' : match; + if (isVideo) { + type = 'WEBM'; + } else { + type = (ref = url.match(/\.([^.]+)$/)) != null ? ref[1].toUpperCase() : void 0; + if (type === 'JPEG') { + type = 'JPG'; + } + } replace = Conf["Replace " + type] && !/spoiler/.test(thumb.src || thumb.dataset.src); if (!(replace || Conf['prefetch'])) { return; @@ -15761,9 +15685,9 @@ ImageLoader = (function() { } file.isPrefetched = true; if (file.videoThumb) { - ref = post.clones; - for (i = 0, len = ref.length; i < len; i++) { - clone = ref[i]; + ref1 = post.clones; + for (i = 0, len = ref1.length; i < len; i++) { + clone = ref1[i]; clone.file.thumb.preload = 'auto'; } thumb.preload = 'auto'; @@ -15777,10 +15701,10 @@ ImageLoader = (function() { el = $.el(isImage ? 'img' : 'video'); if (replace && isImage) { $.on(el, 'load', function() { - var j, len1, ref1; - ref1 = post.clones; - for (j = 0, len1 = ref1.length; j < len1; j++) { - clone = ref1[j]; + var j, len1, ref2; + ref2 = post.clones; + for (j = 0, len1 = ref2.length; j < len1; j++) { + clone = ref2[j]; clone.file.thumb.src = url; } return thumb.src = url; @@ -15860,9 +15784,7 @@ Metadata = (function() { className: 'webm-title' }); el.dataset.index = i; - $.extend(el, { - innerHTML: "" - }); + $.extend(el, {innerHTML: ""}); $.add(file.text, [$.tn(' '), el]); } if (el.children.length === 1) { @@ -16249,9 +16171,7 @@ Volume = (function() { volumeEntry = $.el('label', { title: 'Default volume for videos.' }); - $.extend(volumeEntry, { - innerHTML: " Volume" - }); + $.extend(volumeEntry, {innerHTML: " Volume"}); this.inputs = { unmute: unmuteEntry.firstElementChild, volume: volumeEntry.firstElementChild @@ -16361,20 +16281,18 @@ Embedding = (function() { this.types[type.key] = type; } if (Conf['Embedding'] && g.VIEW !== 'archive') { - this.dialog = UI.dialog('embedding', { - innerHTML: "
      " - }); + this.dialog = UI.dialog('embedding', {innerHTML: "
      "}); this.media = $('#media-embed', this.dialog); $.one(d, '4chanXInitFinished', this.ready); $.on(d, 'IndexRefreshInternal', function() { return g.posts.forEach(function(post) { - var embed, k, l, len1, len2, ref2, ref3; + var embed, l, len1, len2, n, ref2, ref3; ref2 = [post].concat(slice.call(post.clones)); - for (k = 0, len1 = ref2.length; k < len1; k++) { - post = ref2[k]; + for (l = 0, len1 = ref2.length; l < len1; l++) { + post = ref2[l]; ref3 = post.nodes.embedlinks; - for (l = 0, len2 = ref3.length; l < len2; l++) { - embed = ref3[l]; + for (n = 0, len2 = ref3.length; n < len2; n++) { + embed = ref3[n]; Embedding.cb.catalogRemove.call(embed); } } @@ -16382,7 +16300,7 @@ Embedding = (function() { }); } if (Conf['Link Title']) { - return $.on(d, '4chanXInitFinished PostsInserted', function() { + $.on(d, '4chanXInitFinished PostsInserted', function() { var key, ref2, ref3, service; ref2 = Embedding.types; for (key in ref2) { @@ -16392,6 +16310,8 @@ Embedding = (function() { } } }); + this.cache.init(Conf['cachedTitles']); + return $.sync('cachedTitles', this.cache.sync); } }, events: function(post) { @@ -16464,9 +16384,7 @@ Embedding = (function() { embed = $.el('a', { className: 'embedder', href: 'javascript:;' - }, { - innerHTML: "(unembed)" - }); + }, {innerHTML: "(unembed)"}); ref = { key: key, uid: uid, @@ -16521,13 +16439,15 @@ Embedding = (function() { return style.pointerEvents = 'none'; }, title: function(data) { - var key, link, options, post, service, uid; + var key, link, options, post, service, text, uid; key = data.key, uid = data.uid, options = data.options, link = data.link, post = data.post; if (!(service = Embedding.types[key].title)) { return; } $.addClass(link, key.toLowerCase()); - if (service.batchSize) { + if ((text = Embedding.cache.get(data))) { + return Embedding.cb.titleText(text, data); + } else if (service.batchSize) { (service.queue || (service.queue = [])).push(data); if (service.queue.length >= service.batchSize) { return Embedding.flushTitles(service); @@ -16562,6 +16482,70 @@ Embedding = (function() { return results; })()), cb); }, + cache: (function() { + var get, init, newEntries, save, set, sync, titles; + titles = $.dict(); + newEntries = []; + init = function(data) { + var j, key, len, ref, text, uid; + try { + for (j = 0, len = data.length; j < len; j++) { + ref = data[j], key = ref.key, uid = ref.uid, text = ref.text; + titles[key + "." + uid] = text; + } + } catch (error) {} + }; + sync = function(data) { + var j, k, key, len, ref, text, uid; + try { + for (j = 0, len = data.length; j < len; j++) { + ref = data[j], key = ref.key, uid = ref.uid, text = ref.text; + k = key + "." + uid; + if (k in titles) { + break; + } + titles[k] = text; + } + } catch (error) {} + }; + get = function(arg) { + var key, uid; + key = arg.key, uid = arg.uid; + return titles[key + "." + uid]; + }; + set = function(arg, text) { + var key, uid; + key = arg.key, uid = arg.uid; + titles[key + "." + uid] = text; + newEntries.push({ + key: key, + uid: uid, + text: text + }); + return save(); + }; + save = $.debounce(2 * $.SECOND, function() { + return $.get('cachedTitles', Conf['cachedTitles'], function(arg) { + var cachedTitles; + cachedTitles = arg.cachedTitles; + sync(cachedTitles); + try { + cachedTitles = newEntries.concat(cachedTitles).slice(-100); + } catch (error) { + cachedTitles = newEntries; + } + newEntries = []; + return $.set('cachedTitles', cachedTitles); + }); + }); + return { + init: init, + sync: sync, + get: get, + set: set, + save: save + }; + })(), preview: function(data) { var key, link, service, uid; key = data.key, uid = data.uid, link = data.link; @@ -16627,34 +16611,42 @@ Embedding = (function() { } }, title: function(req, data) { - var base1, j, k, key, len, len1, link, link2, options, post, post2, ref, ref1, service, status, text, uid; + var key, service, status, text, uid; if (!req.status) { return; } - key = data.key, uid = data.uid, options = data.options, link = data.link, post = data.post; + key = data.key, uid = data.uid; status = req.status; service = Embedding.types[key].title; - text = "[" + key + "] " + ((function() { - switch (status) { - case 200: - case 304: - return service.text(req.response, uid); - case 404: - return "Not Found"; - case 403: - return "Forbidden or Private"; - default: - return status + "'d"; - } - })()); + switch (status) { + case 200: + case 304: + text = service.text(req.response, uid); + Embedding.cache.set(data, text); + break; + case 404: + text = "Not Found"; + break; + case 403: + text = "Forbidden or Private"; + break; + default: + text = status + "'d"; + } + return Embedding.cb.titleText(text, data); + }, + titleText: function(text, data) { + var base1, j, key, l, len, len1, link, link2, post, post2, ref, ref1; + key = data.key, link = data.link, post = data.post; + text = "[" + key + "] " + text; link.dataset.original = link.textContent; link.textContent = text; ref = post.clones; for (j = 0, len = ref.length; j < len; j++) { post2 = ref[j]; ref1 = $$('a.linkify', post2.nodes.comment); - for (k = 0, len1 = ref1.length; k < len1; k++) { - link2 = ref1[k]; + for (l = 0, len1 = ref1.length; l < len1; l++) { + link2 = ref1[l]; if (!(link2.href === link.href)) { continue; } @@ -16683,9 +16675,7 @@ Embedding = (function() { regExp: /^[^?#]+\.(?:gif|png|jpg|jpeg|bmp)(?::\w+)?(?:[?#]|$)/i, style: '', el: function(a) { - return $.el('div', { - innerHTML: "" - }); + return $.el('div', {innerHTML: ""}); } }, { key: 'video', @@ -16845,10 +16835,10 @@ Embedding = (function() { } }, { key: 'Loopvid', - regExp: /^\w+:\/\/(?:www\.)?loopvid.appspot.com\/#?((?:pf|kd|lv|gd|gh|db|dx|nn|cp|wu|ig|ky|mf|m2|pc|1c|pi|ni|wl|ko|gc)\/[\w\-\/]+(?:,[\w\-\/]+)*|fc\/\w+\/\d+|https?:\/\/.+)/, + regExp: /^\w+:\/\/(?:www\.)?loopvid.appspot.com\/#?((?:pf|kd|lv|gd|gh|db|dx|nn|cp|wu|ig|ky|mf|m2|pc|1c|pi|ni|wl|ko|mm|ic|gc)\/[\w\-\/]+(?:,[\w\-\/]+)*|fc\/\w+\/\d+|https?:\/\/.+)/, style: 'max-width: 80vw; max-height: 80vh;', el: function(a) { - var _, base, el, host, j, k, l, len, len1, len2, name, names, ref, ref1, type, types, url, urls; + var _, base, el, host, j, l, len, len1, len2, n, name, names, ref, ref1, type, types, url, urls; el = $.el('video', { controls: true, preload: 'auto', @@ -16876,8 +16866,8 @@ Embedding = (function() { ref1 = names.split(','); for (j = 0, len = ref1.length; j < len; j++) { name = ref1[j]; - for (k = 0, len1 = types.length; k < len1; k++) { - type = types[k]; + for (l = 0, len1 = types.length; l < len1; l++) { + type = types[l]; base = "" + name + type; urls = (function() { switch (host) { @@ -16910,25 +16900,29 @@ Embedding = (function() { case 'm2': return ["https://kastden.org/_loopvid_media/m2/" + base]; case 'pc': - return ["http://a.pomf.cat/" + base]; + return ["https://kastden.org/_loopvid_media/pc/" + base, "https://web.archive.org/web/2/http://a.pomf.cat/" + base]; case '1c': return ["http://b.1339.cf/" + base]; case 'pi': - return ["https://u.pomf.is/" + base]; + return ["https://kastden.org/_loopvid_media/pi/" + base, "https://web.archive.org/web/2/https://u.pomf.is/" + base]; case 'ni': - return ["https://u.nya.is/" + base]; + return ["https://kastden.org/_loopvid_media/ni/" + base, "https://web.archive.org/web/2/https://u.nya.is/" + base]; case 'wl': return ["http://webm.land/media/" + base]; case 'ko': return ["https://kordy.kastden.org/loopvid/" + base]; + case 'mm': + return ["https://kastden.org/_loopvid_media/mm/" + base, "https://web.archive.org/web/2/https://my.mixtape.moe/" + base]; + case 'ic': + return ["https://media.8ch.net/file_store/" + base]; case 'fc': return ["//" + (ImageHost.host()) + "/" + base + ".webm"]; case 'gc': return ["https://" + type + ".gfycat.com/" + name + ".webm"]; } })(); - for (l = 0, len2 = urls.length; l < len2; l++) { - url = urls[l]; + for (n = 0, len2 = urls.length; n < len2; n++) { + url = urls[n]; $.add(el, $.el('source', { src: url })); @@ -17567,9 +17561,7 @@ DeleteLink = (function() { } link.textContent = DeleteLink.linkText(fileOnly); if (resDoc.title === '4chan - Banned') { - el = $.el('span', { - innerHTML: "You can't delete posts because you are banned." - }); + el = $.el('span', {innerHTML: "You can't delete posts because you are banned."}); return new Notice('warning', el, 20); } else if (msg = resDoc.getElementById('errmsg')) { new Notice('warning', msg.textContent, 20); @@ -17681,9 +17673,7 @@ Menu = (function() { className: 'menu-button', href: 'javascript:;' }); - $.extend(this.button, { - innerHTML: "" - }); + $.extend(this.button, {innerHTML: ""}); this.menu = new UI.Menu('post'); Callbacks.Post.push({ name: 'Menu', @@ -18542,14 +18532,10 @@ FileInfo = (function() { var a, i, j, len, len1, output, ref, ref1; output = []; formatString.replace(/%(.)|[^%]+/g, function(s, c) { - output.push($.hasOwn(FileInfo.formatters, c) ? FileInfo.formatters[c].call(post) : { - innerHTML: E(s) - }); + output.push($.hasOwn(FileInfo.formatters, c) ? FileInfo.formatters[c].call(post) : {innerHTML: E(s)}); return ''; }); - $.extend(outputNode, { - innerHTML: E.cat(output) - }); + $.extend(outputNode, {innerHTML: E.cat(output)}); ref = $$('.download-button', outputNode); for (i = 0, len = ref.length; i < len; i++) { a = ref[i]; @@ -18563,93 +18549,59 @@ FileInfo = (function() { }, formatters: { t: function() { - return { - innerHTML: E(this.file.url.match(/[^/]*$/)[0]) - }; + return {innerHTML: E(this.file.url.match(/[^/]*$/)[0])}; }, T: function() { - return { - innerHTML: "" + (FileInfo.formatters.t.call(this)).innerHTML + "" - }; + return {innerHTML: "" + (FileInfo.formatters.t.call(this)).innerHTML + ""}; }, l: function() { - return { - innerHTML: "" + (FileInfo.formatters.n.call(this)).innerHTML + "" - }; + return {innerHTML: "" + (FileInfo.formatters.n.call(this)).innerHTML + ""}; }, L: function() { - return { - innerHTML: "" + (FileInfo.formatters.N.call(this)).innerHTML + "" - }; + return {innerHTML: "" + (FileInfo.formatters.N.call(this)).innerHTML + ""}; }, n: function() { var fullname, shortname; fullname = this.file.name; shortname = g.SITE.Build.shortFilename(this.file.name, this.isReply); if (fullname === shortname) { - return { - innerHTML: E(fullname) - }; + return {innerHTML: E(fullname)}; } else { - return { - innerHTML: "" + E(shortname) + "" + E(fullname) + "" - }; + return {innerHTML: "" + E(shortname) + "" + E(fullname) + ""}; } }, N: function() { - return { - innerHTML: E(this.file.name) - }; + return {innerHTML: E(this.file.name)}; }, d: function() { - return { - innerHTML: "" - }; + return {innerHTML: ""}; }, f: function() { - return { - innerHTML: "" - }; + return {innerHTML: ""}; }, p: function() { - return { - innerHTML: ((this.file.isSpoiler) ? "Spoiler, " : "") - }; + return {innerHTML: ((this.file.isSpoiler) ? "Spoiler, " : "")}; }, s: function() { - return { - innerHTML: E(this.file.size) - }; + return {innerHTML: E(this.file.size)}; }, B: function() { - return { - innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes" - }; + return {innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes"}; }, K: function() { - return { - innerHTML: E(Math.round(this.file.sizeInBytes/1024)) + " KB" - }; + return {innerHTML: E(Math.round(this.file.sizeInBytes/1024)) + " KB"}; }, M: function() { - return { - innerHTML: E(Math.round(this.file.sizeInBytes/1048576*100)/100) + " MB" - }; + return {innerHTML: E(Math.round(this.file.sizeInBytes/1048576*100)/100) + " MB"}; }, r: function() { - return { - innerHTML: E(this.file.dimensions || "PDF") - }; + return {innerHTML: E(this.file.dimensions || "PDF")}; }, g: function() { - return { - innerHTML: ((this.file.tag) ? ", " + E(this.file.tag) : "") - }; + return {innerHTML: ((this.file.tag) ? ", " + E(this.file.tag) : "")}; }, '%': function() { - return { - innerHTML: "%" - }; + return {innerHTML: "%"}; } } }; @@ -18718,7 +18670,17 @@ Fourchan = (function() { return $.addClass(pre, 'prettyprinted'); } }); - $.globalEval('window.addEventListener(\'prettyprint\', function(e) {\n window.dispatchEvent(new CustomEvent(\'prettyprint:cb\', {\n detail: {\n ID: e.detail.ID,\n i: e.detail.i,\n html: prettyPrintOne(e.detail.html)\n }\n }));\n}, false);'); + $.global(function() { + return window.addEventListener('prettyprint', function(e) { + return window.dispatchEvent(new CustomEvent('prettyprint:cb', { + detail: { + ID: e.detail.ID, + i: e.detail.i, + html: window.prettyPrintOne(e.detail.html) + } + })); + }, false); + }); Callbacks.Post.push({ name: 'Parse [code] tags', cb: Fourchan.code @@ -19317,7 +19279,7 @@ Keybinds = (function() { } post = Keybinds.post(threadRoot); Keybinds.hl(+1, threadRoot); - Filter.quickFilterMD5.call(post); + Filter.quickFilterMD5.call(post, e); break; case Conf['Previous Post Quoting You']: if (!(threadRoot && QuoteYou.db)) { @@ -19531,34 +19493,24 @@ ModContact = (function() { } }, template: function(capcode) { - return { - innerHTML: "feedback" + (ModContact.specific[capcode]()).innerHTML - }; + return {innerHTML: "feedback" + (ModContact.specific[capcode]()).innerHTML}; }, specific: { Mod: function() { - return { - innerHTML: " IRC" - }; + return {innerHTML: " IRC"}; }, Manager: function() { return ModContact.specific.Mod(); }, Developer: function() { - return { - innerHTML: " github" - }; + return {innerHTML: " github"}; }, Admin: function() { - return { - innerHTML: " twitter" - }; + return {innerHTML: " twitter"}; } }, moveNote: { - qa: { - innerHTML: "Moving a thread to /qa/ does not imply mods will read it. If you wish to contact mods, use feedback (https://www.4chan.org/feedback) or IRC (https://www.4chan-x.net/4chan-irc.html)." - } + qa: {innerHTML: "Moving a thread to /qa/ does not imply mods will read it. If you wish to contact mods, use feedback (https://www.4chan.org/feedback) or IRC (https://www.4chan-x.net/4chan-irc.html)."} } }; @@ -19889,9 +19841,7 @@ PostJumper = (function() { span = $.el('span', { className: 'postJumper' }); - $.extend(span, { - innerHTML: "" + E(charPrev) + "" + E(charNext) + "" - }); + $.extend(span, {innerHTML: "" + E(charPrev) + "" + E(charNext) + ""}); return span; }, scroll: function(fromJumper, toJumper) { @@ -20121,9 +20071,7 @@ Report = (function() { fieldset = $.el('fieldset', { id: 'archive-report', hidden: true - }, { - innerHTML: "" - }); + }, {innerHTML: ""}); enabled = $('#archive-report-enabled', fieldset); reason = $('#archive-report-reason', fieldset); submit = $('#archive-report-submit', fieldset); @@ -20590,9 +20538,7 @@ ReplyPruning = (function() { label = UI.checkbox('Prune Replies', 'Show Last', Conf['Prune All Threads']); el = $.el('span', { title: 'Maximum number of replies to show.' - }, { - innerHTML: " " - }); + }, {innerHTML: " "}); $.prepend(el, label); this.inputs = { enabled: label.firstElementChild, @@ -20736,9 +20682,7 @@ ThreadStats = (function() { if (Conf['Page Count in Stats']) { this[(typeof (base = g.SITE).isPrunedByAge === "function" ? base.isPrunedByAge(g.BOARD) : void 0) ? 'showPurgePos' : 'showPage'] = true; } - statsHTML = { - innerHTML: "? / ?" + ((Conf["IP Count in Stats"] && g.SITE.hasIPCount) ? " / ?" : "") + ((Conf["Page Count in Stats"]) ? " / ?" : "") - }; + statsHTML = {innerHTML: "? / ?" + ((Conf["IP Count in Stats"] && g.SITE.hasIPCount) ? " / ?" : "") + ((Conf["Page Count in Stats"]) ? " / ?" : "")}; statsTitle = 'Posts / Files'; if (Conf['IP Count in Stats'] && g.SITE.hasIPCount) { statsTitle += ' / IPs'; @@ -20754,9 +20698,7 @@ ThreadStats = (function() { $.extend(sc, statsHTML); Header.addShortcut('stats', sc, 200); } else { - this.dialog = sc = UI.dialog('thread-stats', { - innerHTML: "
      " + (statsHTML).innerHTML + "
      " - }); + this.dialog = sc = UI.dialog('thread-stats', {innerHTML: "
      " + (statsHTML).innerHTML + "
      "}); $.addClass(doc, 'float'); $.ready(function() { return $.add(d.body, sc); @@ -20925,14 +20867,10 @@ ThreadUpdater = (function() { this.dialog = sc = $.el('span', { id: 'updater' }); - $.extend(sc, { - innerHTML: "" - }); + $.extend(sc, {innerHTML: ""}); Header.addShortcut('updater', sc, 100); } else { - this.dialog = sc = UI.dialog('updater', { - innerHTML: "
      " - }); + this.dialog = sc = UI.dialog('updater', {innerHTML: "
      "}); $.addClass(doc, 'float'); $.ready(function() { return $.add(d.body, sc); @@ -20946,9 +20884,7 @@ ThreadUpdater = (function() { updateLink = $.el('span', { className: 'brackets-wrap updatelink' }); - $.extend(updateLink, { - innerHTML: "Update" - }); + $.extend(updateLink, {innerHTML: "Update"}); Main.ready(function() { var navLinksBot; if ((navLinksBot = $('.navLinksBot'))) { @@ -20974,9 +20910,7 @@ ThreadUpdater = (function() { el: el }); } - this.settings = $.el('span', { - innerHTML: "Interval" - }); + this.settings = $.el('span', {innerHTML: "Interval"}); $.on(this.settings, 'click', this.intervalShortcut); subEntries.push({ el: this.settings @@ -21359,9 +21293,7 @@ ThreadWatcher = (function() { }); this.db = new DataBoard('watchedThreads', this.refresh, true); this.dbLM = new DataBoard('watcherLastModified', null, true); - this.dialog = UI.dialog('thread-watcher', { - innerHTML: "
      Thread Watcher ×
      " - }); + this.dialog = UI.dialog('thread-watcher', {innerHTML: "
      Thread Watcher ×
      "}); this.status = $('#watcher-status', this.dialog); this.list = this.dialog.lastElementChild; this.refreshButton = $('.refresh', this.dialog); @@ -21399,9 +21331,7 @@ ThreadWatcher = (function() { el: $.el('a', { href: 'javascript:;', className: 'has-shortcut-text' - }, { - innerHTML: "Alt+click" - }), + }, {innerHTML: "Alt+click"}), order: 6, open: function(arg) { var thread; @@ -23486,9 +23416,7 @@ Captcha = {}; root = $.el('div', { className: 'captcha-root' }); - $.extend(root, { - innerHTML: "
      " - }); + $.extend(root, {innerHTML: "
      "}); counter = $('.captcha-counter > a', root); this.nodes = { root: root, @@ -23767,9 +23695,7 @@ PassLink = (function() { passLink = $.el('span', { className: 'brackets-wrap pass-link-container' }); - $.extend(passLink, { - innerHTML: "4chan Pass" - }); + $.extend(passLink, {innerHTML: "4chan Pass"}); $.on(passLink.firstElementChild, 'click', function() { return window.open("//sys." + (location.hostname.split('.')[1]) + ".org/auth", Date.now(), 'width=500,height=280,toolbar=0'); }); @@ -23940,9 +23866,7 @@ QR = (function() { link = $.el('h1', { className: "qr-link-container" }); - $.extend(link, { - innerHTML: "" + ((g.VIEW === "thread") ? "Reply to Thread" : "Start a Thread") + "" - }); + $.extend(link, {innerHTML: "" + ((g.VIEW === "thread") ? "Reply to Thread" : "Start a Thread") + ""}); QR.link = link.firstElementChild; $.on(link.firstChild, 'click', function() { QR.open(); @@ -23955,9 +23879,7 @@ QR = (function() { linkBot = $.el('div', { className: "brackets-wrap qr-link-container-bottom" }); - $.extend(linkBot, { - innerHTML: "Reply to Thread" - }); + $.extend(linkBot, {innerHTML: "Reply to Thread"}); $.on(linkBot.firstElementChild, 'click', function() { QR.open(); return QR.nodes.com.focus(); @@ -24156,9 +24078,7 @@ QR = (function() { } }, connectionError: function() { - return $.el('span', { - innerHTML: "Connection error while posting. [More info]" - }); + return $.el('span', {innerHTML: "Connection error while posting. [More info]"}); }, notifications: [], cleanNotifications: function() { @@ -24306,9 +24226,7 @@ QR = (function() { openError: function() { var div; div = $.el('div'); - $.extend(div, { - innerHTML: "Could not open file. [More info]" - }); + $.extend(div, {innerHTML: "Could not open file. [More info]"}); return QR.error(div); }, setFile: function(e) { @@ -24486,9 +24404,7 @@ QR = (function() { dialog: function() { var classList, config, dialog, event, i, items, name, node, nodes, save, setNode; QR.nodes = nodes = { - el: dialog = UI.dialog('qr', { - innerHTML: "
      ×
      No selected file
      " - }) + el: dialog = UI.dialog('qr', {innerHTML: "
      ×
      No selected file
      "}) }; setNode = function(name, query) { return nodes[name] = $(query, dialog); @@ -24544,6 +24460,7 @@ QR = (function() { QR.flagsInput(); $.on(nodes.autohide, 'change', QR.toggleHide); $.on(nodes.close, 'click', QR.close); + $.on(nodes.status, 'click', QR.submit); $.on(nodes.form, 'submit', QR.submit); $.on(nodes.sjisToggle, 'click', QR.toggleSJIS); $.on(nodes.texButton, 'mousedown', QR.texPreviewShow); @@ -24657,19 +24574,24 @@ QR = (function() { } }, submit: function(e) { - var captcha, cb, err, filetag, formData, options, post, ref, thread, threadID; + var captcha, cb, err, filetag, force, formData, options, post, ref, thread, threadID; if (e != null) { e.preventDefault(); } + force = e != null ? e.shiftKey : void 0; if (QR.req) { QR.abort(); return; } $.forceSync('cooldowns'); if (QR.cooldown.seconds) { - QR.cooldown.auto = !QR.cooldown.auto; - QR.status(); - return; + if (force) { + QR.cooldown.clear(); + } else { + QR.cooldown.auto = !QR.cooldown.auto; + QR.status(); + return; + } } post = QR.posts[0]; post.forceSave(); @@ -24703,7 +24625,7 @@ QR = (function() { } } QR.cleanNotifications(); - if (err) { + if (err && !force) { QR.cooldown.auto = false; QR.status(); QR.error(err); @@ -24789,7 +24711,7 @@ QR = (function() { return QR.status(); }, response: function() { - var URL, _, connErr, err, h1, isReply, lastPostToThread, m, open, post, postID, postsCount, ref, ref1, ref2, ref3, seconds, threadID; + var URL, _, connErr, err, h1, isReply, j, lastPostToThread, len, m, mi, open, post, postID, postsCount, ref, ref1, ref2, ref3, seconds, threadID; if (this !== QR.req) { return; } @@ -24823,9 +24745,13 @@ QR = (function() { QR.cooldown.auto = QR.captcha.isEnabled || connErr; QR.cooldown.addDelay(post, 2); } - } else if (err.textContent && (m = err.textContent.match(/(?:(\d+)\s+minutes?\s+)?(\d+)\s+second/i)) && !/duplicate|hour/i.test(err.textContent)) { + } else if (err.textContent && (m = err.textContent.match(/\d+\s+(?:minute|second)/gi)) && !/duplicate|hour/i.test(err.textContent)) { QR.cooldown.auto = !/have\s+been\s+muted/i.test(err.textContent); - seconds = 60 * (+(m[1] || 0)) + (+m[2]); + seconds = 0; + for (j = 0, len = m.length; j < len; j++) { + mi = m[j]; + seconds += (/minute/i.test(mi) ? 60 : 1) * (+mi.match(/\d+/)[0]); + } if (/muted/i.test(err.textContent)) { QR.cooldown.addMute(seconds); } else { @@ -24858,10 +24784,10 @@ QR = (function() { postsCount = QR.posts.length - 1; QR.cooldown.auto = postsCount && isReply; lastPostToThread = !((function() { - var j, len, p, ref4; + var k, len1, p, ref4; ref4 = QR.posts.slice(1); - for (j = 0, len = ref4.length; j < len; j++) { - p = ref4[j]; + for (k = 0, len1 = ref4.length; k < len1; k++) { + p = ref4[k]; if (p.thread === post.thread) { return true; } @@ -25102,6 +25028,13 @@ QR = (function() { }); }); }, + clear: function() { + QR.cooldown.data = $.dict(); + QR.cooldown.changes = $.dict(); + QR.cooldown.auto = false; + QR.cooldown.update(); + return $.queueTask($["delete"], 'cooldowns'); + }, update: function() { var base, cooldown, cooldowns, elapsed, i, len, maxDelay, nCooldowns, now, ref, ref1, save, scope, seconds, start, suffix, threadID, type, update; if (!QR.cooldown.isCounting) { @@ -25524,9 +25457,7 @@ QR = (function() { draggable: true, href: 'javascript:;' }); - $.extend(el, { - innerHTML: "" - }); + $.extend(el, {innerHTML: ""}); this.nodes = { el: el, rm: el.firstChild, @@ -25763,9 +25694,7 @@ QR = (function() { div = $.el('div', { className: className }); - $.extend(div, { - innerHTML: E(message) + ((link) ? " [More info]" : "") + "
      [delete post] [delete all]" - }); + $.extend(div, {innerHTML: E(message) + ((link) ? " [More info]" : "") + "
      [delete post] [delete all]"}); (this.errors || (this.errors = [])).push(div); ref = $$('a', div), rm = ref[0], rmAll = ref[1]; $.on(div, 'click', (function(_this) { @@ -26568,16 +26497,12 @@ QuoteThreading = if (!(Conf['Quote Threading'] && g.VIEW === 'thread')) { return; } - this.controls = $.el('label', { - innerHTML: " Threading" - }); + this.controls = $.el('label', {innerHTML: " Threading"}); this.threadNewLink = $.el('span', { className: 'brackets-wrap threadnewlink', hidden: true }); - $.extend(this.threadNewLink, { - innerHTML: "Thread New Posts" - }); + $.extend(this.threadNewLink, {innerHTML: "Thread New Posts"}); this.input = $('input', this.controls); this.input.checked = Conf['Thread Quotes']; $.on(this.input, 'change', this.setEnabled); @@ -26872,9 +26797,7 @@ QuoteYou = (function() { var input, label, ref; label = $.el('label', { className: 'toggle-you' - }, { - innerHTML: " You" - }); + }, {innerHTML: " You"}); input = $('input', label); $.on(input, 'change', QuoteYou.menu.toggle); return (ref = Menu.menu) != null ? ref.addEntry({ @@ -27276,6 +27199,7 @@ Main = (function() { if ($.cantSet) { } else if (items.previousversion == null) { + Main.isFirstRun = true; Main.ready(function() { $.set('previousversion', g.VERSION); return Settings.open(); @@ -27299,9 +27223,7 @@ Main = (function() { return $.set(changes, function() { var el, ref; if ((ref = items['Show Updated Notifications']) != null ? ref : true) { - el = $.el('span', { - innerHTML: "4chan X has been updated to version " + E(g.VERSION) + "." - }); + el = $.el('span', {innerHTML: "4chan X has been updated to version " + E(g.VERSION) + "."}); return new Notice('info', el, 15); } }); @@ -27541,9 +27463,7 @@ Main = (function() { return; } if (typeof (base1 = g.SITE).isIncomplete === "function" ? base1.isIncomplete() : void 0) { - msg = $.el('div', { - innerHTML: "The page didn't load completely.
      Some features may not work unless you reload." - }); + msg = $.el('div', {innerHTML: "The page didn't load completely.
      Some features may not work unless you reload."}); $.on($('a', msg), 'click', function() { return location.reload(); }); @@ -27627,7 +27547,8 @@ Main = (function() { err = error1; errors.push({ message: "Parsing of Post No." + (postRoot.id.match(/\d+/)) + " failed. Post will be skipped.", - error: err + error: err, + html: postRoot.outerHTML }); } } @@ -27746,7 +27667,8 @@ Main = (function() { err = error1; errors.push({ message: "Parsing of Catalog Thread No." + ((threadRoot.dataset.id || threadRoot.id).match(/\d+/)) + " failed. Thread will be skipped.", - error: err + error: err, + html: threadRoot.outerHTML }); } } @@ -27810,11 +27732,21 @@ Main = (function() { return softTask(); }, handleErrors: function(errors) { - var div, error, j, len, logs; + var div, enabled, error, j, len, logs, msg; if (d.body && $.hasClass(d.body, 'fourchan_x') && !$.hasClass(doc, 'tainted')) { new Notice('error', 'Error: Multiple copies of 4chan X are enabled.'); $.addClass(doc, 'tainted'); } + if (g.SITE.testNativeExtension && !$.hasClass(doc, 'tainted')) { + enabled = g.SITE.testNativeExtension().enabled; + if (enabled) { + $.addClass(doc, 'tainted'); + if (Conf['Disable Native Extension'] && !Main.isFirstRun) { + msg = $.el('div', {innerHTML: "Failed to disable the native extension. You may need to block it."}); + new Notice('error', msg); + } + } + } if (!(errors instanceof Array)) { error = errors; } else if (errors.length === 1) { @@ -27824,9 +27756,7 @@ Main = (function() { new Notice('error', Main.parseError(error, Main.reportLink([error])), 15); return; } - div = $.el('div', { - innerHTML: E(errors.length) + " errors occurred." + (Main.reportLink(errors)).innerHTML + " [show]" - }); + div = $.el('div', {innerHTML: E(errors.length) + " errors occurred." + (Main.reportLink(errors)).innerHTML + " [show]"}); $.on(div.lastElementChild, 'click', function() { var ref; return ref = this.textContent === 'show' ? ['hide', false] : ['show', true], this.textContent = ref[0], logs.hidden = ref[1], ref; @@ -27843,9 +27773,7 @@ Main = (function() { parseError: function(data, reportLink) { var context, error, lines, message, ref, ref1; c.error(data.message, data.error.stack); - message = $.el('div', { - innerHTML: E(data.message) + ((reportLink) ? (reportLink).innerHTML : "") - }); + message = $.el('div', {innerHTML: E(data.message) + ((reportLink) ? (reportLink).innerHTML : "")}); error = $.el('div', { textContent: (data.error.name || 'Error') + ": " + (data.error.message || 'see console for details') }); @@ -27856,7 +27784,7 @@ Main = (function() { return [message, error, context]; }, reportLink: function(errors) { - var addDetails, data, details, title, url; + var addDetails, data, details, info, title, url; data = errors[0]; title = data.message; if (errors.length > 1) { @@ -27868,7 +27796,10 @@ Main = (function() { return details += text + '\n'; } }; - addDetails("[Please describe the steps needed to reproduce this error.]\n\nScript: 4chan X ccd0 v" + g.VERSION + " " + $.platform + "\nUser agent: " + navigator.userAgent + "\nURL: " + location.href); + addDetails("[Please describe the steps needed to reproduce this error.]\n\nScript: 4chan X ccd0 v" + g.VERSION + " " + $.platform + "\nURL: " + location.href + "\nUser agent: " + navigator.userAgent); + if ($.platform === 'userscript' && (info = typeof GM !== "undefined" && GM !== null ? GM.info : (typeof GM_info !== "undefined" && GM_info !== null ? GM_info : void 0))) { + addDetails("Userscript manager: " + info.scriptHandler + " " + info.version); + } addDetails('\n' + data.error); if (data.error.stack) { addDetails(data.error.stack.replace(data.error.toString(), '').trim()); @@ -27877,10 +27808,8 @@ Main = (function() { addDetails('\n`' + data.html + '`'); } details = details.replace(/file:\/{3}.+\//g, ''); - url = "https://gitreports.com/issue/ccd0/4chan-x?issue_title=" + (encodeURIComponent(title)) + "&details=" + (encodeURIComponent(details)); - return { - innerHTML: " [report]" - }; + url = 'https://gitreports.com/issue/ccd0/4chan-x?issue_title=%title&details=%details'.replace('%title', encodeURIComponent(title)).replace('%details', encodeURIComponent(details)); + return {innerHTML: " [report]"}; }, isThisPageLegit: function() { if (!('thisPageIsLegit' in Main)) { diff --git a/builds/4chan-X.zip b/builds/4chan-X.zip index 1725a4ca7..5a4a0b6ea 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 49926a1cc..7987f4264 100644 --- a/builds/updates-beta.json +++ b/builds/updates-beta.json @@ -3,7 +3,7 @@ "4chan-x@4chan-x.net": { "updates": [ { - "version": "1.14.11.1", + "version": "1.14.12.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 7c2804231..1c2d10b63 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 5f6b080fc..e0b313ec6 100644 --- a/builds/updates.json +++ b/builds/updates.json @@ -3,7 +3,7 @@ "4chan-x@4chan-x.net": { "updates": [ { - "version": "1.14.11.1", + "version": "1.14.12.0", "update_link": "https://www.4chan-x.net/builds/4chan-X.crx" } ] diff --git a/builds/updates.xml b/builds/updates.xml index f274a4127..2210c2c98 100644 --- a/builds/updates.xml +++ b/builds/updates.xml @@ -1,7 +1,7 @@ - + diff --git a/version.json b/version.json index 4cc5217a9..be0d9ccaf 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { - "version": "1.14.11.1", - "date": "2019-08-03T18:01:40.027Z" + "version": "1.14.12.0", + "date": "2019-08-05T05:48:40.635Z" } \ No newline at end of file