NoShitEmpornium

Fully featured torrent filtering solution for Empornium

  1. // ==UserScript==
  2. // @name NoShitEmpornium
  3. // @namespace http://www.empornium.me/
  4. // @version 2.7.12
  5. // @license GPLv3
  6. // @description Fully featured torrent filtering solution for Empornium
  7. // @supportURL https://github.com/ceodoe/noshitempornium/issues
  8. // @homepageURL https://github.com/ceodoe/noshitempornium/
  9. // @icon https://www.google.com/s2/favicons?domain=empornium.is
  10. // @author ceodoe
  11. // @include /^https?://www\.empornium\.(me|sx|is)/torrents\.php*/
  12. // @include /^https?://www\.empornium\.(me|sx|is)/collage\/*
  13. // @include /^https?://www\.empornium\.(me|sx|is)/top10\.php*/
  14. // @include /^https?://www\.empornium\.(me|sx|is)/user\.php\?action=notify/
  15. // @include /^https?://www\.empornium\.(me|sx|is)/requests\.php/
  16. // @exclude /^https?://www\.empornium\.(me|sx|is)/top10\.php.*(\?|&)(type=(users|tags|taggers))/
  17. // @run-at document-end
  18. // @grant GM_getValue
  19. // @grant GM_setValue
  20. // @grant GM_listValues
  21. // @grant GM_deleteValue
  22. // @grant GM_addStyle
  23. // ==/UserScript==
  24. //
  25. // Copyright © 2015-2024 ceodoe
  26. //
  27. // This program is free software: you can redistribute it and/or modify
  28. // it under the terms of the GNU General Public License as published by
  29. // the Free Software Foundation, either version 3 of the License, or
  30. // (at your option) any later version.
  31. //
  32. // This program is distributed in the hope that it will be useful,
  33. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  34. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  35. // GNU General Public License for more details.
  36. //
  37. // You should have received a copy of the GNU General Public License
  38. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  39.  
  40.  
  41. // +----------------------------+
  42. // | Loading and initialization |
  43. // +----------------------------+
  44.  
  45. // Get version information
  46. let nseVersion = GM_info.script.version.split(".");
  47. let nseVersionNum = Number(nseVersion[0] + nseVersion[1].padStart(2, "0") + nseVersion[2].padStart(2, "0"));
  48. let nseSavedVersion = GM_getValue("nseSavedVersion", nseVersionNum);
  49. GM_setValue("nseSavedVersion", nseSavedVersion);
  50.  
  51. // Load and initialize saved filter lists
  52. let nseBlacklistTaglist = GM_getValue("nseTaglist", "enter.tags.here separated.by.spaces no.newlines").trim();
  53. let nseBlacklistTags = nseBlacklistTaglist.split(" ");
  54. nseBlacklistTags.sort();
  55. nseBlacklistTaglist = nseBlacklistTags.join(" ");
  56. if(nseBlacklistTags.length === 1 && nseBlacklistTags[0] === "") { nseBlacklistTags = new Array(0); }
  57. GM_setValue("nseTaglist", nseBlacklistTaglist);
  58.  
  59. let nseHardPassTaglist = GM_getValue("nseHardPassTaglist", "enter.hard.pass.tags.here big.poopies gushing.blood").trim();
  60. let nseHardPassTags = nseHardPassTaglist.split(" ");
  61. nseHardPassTags.sort();
  62. nseHardPassTaglist = nseHardPassTags.join(" ");
  63. if(nseHardPassTags.length === 1 && nseHardPassTags[0] === "") { nseHardPassTags = new Array(0); }
  64. GM_setValue("nseHardPassTaglist", nseHardPassTaglist);
  65.  
  66. let nseWhitelistTaglist = GM_getValue("nseWhitelist", "whitelist.tags go.here").trim();
  67. let nseWhitelistTags = nseWhitelistTaglist.split(" ");
  68. nseWhitelistTags.sort();
  69. nseWhitelistTaglist = nseWhitelistTags.join(" ");
  70. if(nseWhitelistTags.length === 1 && nseWhitelistTags[0] === "") { nseWhitelistTags = new Array(0); }
  71. GM_setValue("nseWhitelist", nseWhitelistTaglist);
  72.  
  73. let nseBlacklistTitleList = GM_getValue("nseBlacklistTitles", "this is a title phrase;this is another title phrase").trim();
  74. let nseBlacklistTitlePhrases = nseBlacklistTitleList.split(";");
  75. nseBlacklistTitlePhrases.sort();
  76. nseBlacklistTitleList = nseBlacklistTitlePhrases.join(";");
  77. if(nseBlacklistTitlePhrases.length === 1 && nseBlacklistTitlePhrases[0] === "") { nseBlacklistTitlePhrases = new Array(0); }
  78. GM_setValue("nseBlacklistTitles", nseBlacklistTitleList);
  79.  
  80. let nseWhitelistTitleList = GM_getValue("nseWhitelistTitles", "this is a title phrase;this is another title phrase").trim();
  81. let nseWhitelistTitlePhrases = nseWhitelistTitleList.split(";");
  82. nseWhitelistTitlePhrases.sort();
  83. nseWhitelistTitleList = nseWhitelistTitlePhrases.join(";");
  84. if(nseWhitelistTitlePhrases.length === 1 && nseWhitelistTitlePhrases[0] === "") { nseWhitelistTitlePhrases = new Array(0); }
  85. GM_setValue("nseWhitelistTitles", nseWhitelistTitleList);
  86.  
  87. let nseBlacklistUploadersList = GM_getValue("nseUploaders", "PutUserNamesHere SeparatedBySpaces NoNewlines").trim();
  88. let nseBlacklistUploaders = nseBlacklistUploadersList.split(" ");
  89. nseBlacklistUploaders.sort();
  90. nseBlacklistUploadersList = nseBlacklistUploaders.join(" ");
  91. if(nseBlacklistUploaders.length === 1 && nseBlacklistUploaders[0] === "") { nseBlacklistUploaders = new Array(0); }
  92. GM_setValue("nseUploaders", nseBlacklistUploadersList);
  93.  
  94. let nseWhitelistUploadersList = GM_getValue("nseWhitelistUploaders", "PutUserNamesHere SeparatedBySpaces NoNewlines").trim();
  95. let nseWhitelistUploaders = nseWhitelistUploadersList.split(" ");
  96. nseWhitelistUploaders.sort();
  97. nseWhitelistUploadersList = nseWhitelistUploaders.join(" ");
  98. if(nseWhitelistUploaders.length === 1 && nseWhitelistUploaders[0] === "") { nseWhitelistUploaders = new Array(0); }
  99. GM_setValue("nseWhitelistUploaders", nseWhitelistUploadersList);
  100.  
  101.  
  102. // Delete obsolete options
  103. GM_deleteValue("nseEnableGCDCompatibilityMode");
  104.  
  105. // Load saved options
  106. // Filtering options
  107. // Individual upload filtering
  108. let nseIndividualUploadHidingEnabled = GM_getValue("nseIndividualUploadHidingEnabled", true);
  109. let nseIndividualUploadHidingBlacklist = GM_getValue("nseIndividualUploadHidingBlacklist", new Array(0));
  110. let nseIndividualUploadHidingWhitelist = GM_getValue("nseIndividualUploadHidingWhitelist", new Array(0));
  111.  
  112. // Filter all button
  113. let nseFilterAllButtonEnabled = GM_getValue("nseFilterAllButtonEnabled", true);
  114.  
  115. // Right-Click Management
  116. let nseRightClickManagementEnabled = GM_getValue("nseRightClickManagementEnabled", true);
  117. let nseRCMTagsEnabled = GM_getValue("nseRCMTagsEnabled", true);
  118. let nseRCMTitlesEnabled = GM_getValue("nseRCMTitlesEnabled", true);
  119. let nseRCMUploadersEnabled = GM_getValue("nseRCMUploadersEnabled", true);
  120.  
  121. // Torrent site status
  122. let nseHideAnonUploadsEnabled = GM_getValue("nseHideAnonUploadsEnabled", false);
  123. let nseHideReportedEnabled = GM_getValue("nseHideReportedEnabled", false);
  124. let nseHideWarnedEnabled = GM_getValue("nseHideWarnedEnabled", false);
  125. let nseHideUnseededEnabled = GM_getValue("nseHideUnseededEnabled", false);
  126.  
  127. // Torrent personal status
  128. let nseHideGrabbedEnabled = GM_getValue("nseHideGrabbedEnabled", false);
  129. let nseHideLeechingEnabled = GM_getValue("nseHideLeechingEnabled", false);
  130. let nseHideSeedingEnabled = GM_getValue("nseHideSeedingEnabled", false);
  131. let nseHideSnatchedEnabled = GM_getValue("nseHideSnatchedEnabled", false);
  132. let nseHideBookmarkedEnabled = GM_getValue("nseHideBookmarkedEnabled", false);
  133. let nseBypassWhitelistsEnabled = GM_getValue("nseBypassWhitelistsEnabled", false);
  134.  
  135. // Hard Pass
  136. let nseHardPassEnabled = GM_getValue("nseHardPassEnabled", false);
  137. let nseRemoveHardPassResults = GM_getValue("nseRemoveHardPassResults", false);
  138.  
  139. // Interface options
  140. // Oblivious mode
  141. let nseObliviousModeEnabled = GM_getValue("nseObliviousModeEnabled", false);
  142.  
  143. // Custom CSS
  144. let nseCustomCSSEnabled = GM_getValue("nseCustomCSSEnabled", false);
  145. let nseCustomCSS = GM_getValue("nseCustomCSS", "/* With great power comes great responsibility */");
  146.  
  147. // Auto-scroll to NSE
  148. let nseScrollToNSEEnabled = GM_getValue("nseScrollToNSEEnabled", false);
  149.  
  150. // Emoji
  151. let nseEmojiEnabled = GM_getValue("nseEmojiEnabled", true);
  152.  
  153. // Update toasts
  154. let nseUpdateToastsEnabled = GM_getValue("nseUpdateToastsEnabled", true);
  155.  
  156. // Floating toggle button
  157. let nseFloatingToggleButtonEnabled = GM_getValue("nseFloatingToggleButtonEnabled", true);
  158.  
  159. // Open all button
  160. let nseOpenAllButtonEnabled = GM_getValue("nseOpenAllButtonEnabled", true);
  161. let nseOpenAllGoNextEnabled = GM_getValue("nseOpenAllGoNextEnabled", false);
  162.  
  163. // Fonts
  164. let nseUIFont = GM_getValue("nseUIFont", "Helvetica");
  165. let nseTextAreaFont = GM_getValue("nseTextAreaFont", "Monospace");
  166.  
  167. // Highlight colors
  168. let nseBlacklistColor = GM_getValue("nseBlacklistColor", "#F00");
  169. let nseHardPassColor = GM_getValue("nseHardPassColor", "#A00");
  170. let nseWhitelistColor = GM_getValue("nseWhitelistColor", "#0F0");
  171.  
  172. // Custom timeout
  173. let nseTimeout = GM_getValue("nseTimeout", 1500);
  174.  
  175. // Theme
  176. let nseSelectedTheme = GM_getValue("nseSelectedTheme", "nseThemeDefault");
  177. let nseCustomTheme = GM_getValue("nseCustomTheme", {
  178. backgroundColor: "#fff",
  179. backgroundHighlightColor: "#cfe7ff",
  180. foregroundColor: "#000",
  181. accentColor: "#0af",
  182. highlightColor: "#0071b0",
  183. hiddenBackgroundColor: "#aaf"
  184. });
  185.  
  186. if(nseCustomTheme.hiddenBackgroundColor == undefined) {
  187. nseCustomTheme.hiddenBackgroundColor = "#aaf";
  188. }
  189.  
  190. let themes = {
  191. nseThemeDefault:
  192. {
  193. backgroundColor: "#fff",
  194. backgroundHighlightColor: "#cfe7ff",
  195. foregroundColor: "#000",
  196. accentColor: "#0af",
  197. highlightColor: "#0071b0",
  198. hiddenBackgroundColor: "#aaf"
  199. },
  200. nseThemeLegacy:
  201. {
  202. backgroundColor: "#00f",
  203. backgroundHighlightColor: "#44f",
  204. foregroundColor: "#fff",
  205. accentColor: "#ddd",
  206. highlightColor: "#fff",
  207. hiddenBackgroundColor: "#aaf"
  208. },
  209. nseThemeEdgy:
  210. {
  211. backgroundColor: "#000",
  212. backgroundHighlightColor: "#333",
  213. foregroundColor: "#f00",
  214. accentColor: "#f22",
  215. highlightColor: "#f22",
  216. hiddenBackgroundColor: "#aaf"
  217. },
  218. nseThemeBaked:
  219. {
  220. backgroundColor: "#8300ff",
  221. backgroundHighlightColor: "#087300",
  222. foregroundColor: "#0a8700",
  223. accentColor: "#b669ff",
  224. highlightColor: "#b669ff",
  225. hiddenBackgroundColor: "#aaf"
  226. },
  227. nseThemeDarkPurple:
  228. {
  229. backgroundColor: "#31363b",
  230. backgroundHighlightColor: "#495057",
  231. foregroundColor: "#ffffff",
  232. accentColor: "#7b68ee",
  233. highlightColor: "#7b68ee",
  234. hiddenBackgroundColor: "#aaf"
  235. },
  236. nseThemeCustom:
  237. {
  238. backgroundColor: nseCustomTheme.backgroundColor,
  239. backgroundHighlightColor: nseCustomTheme.backgroundHighlightColor,
  240. foregroundColor: nseCustomTheme.foregroundColor,
  241. accentColor: nseCustomTheme.accentColor,
  242. highlightColor: nseCustomTheme.highlightColor,
  243. hiddenBackgroundColor: nseCustomTheme.hiddenBackgroundColor
  244. }
  245. };
  246.  
  247. let nseThemeDescriptions = {
  248. nseThemeDefault: "White background with black text and blue accents",
  249. nseThemeLegacy: "Ye Olde Theme with a blue background and white text",
  250. nseThemeEdgy: "For the edgelord in all of us, red text on a black background",
  251. nseThemeBaked: "Ayyyy 420 blaze it &mdash; Green and purple",
  252. nseThemeDarkPurple: "A dark mode theme with purple highlights",
  253. nseThemeCustom: "Define your own colors using the text boxes below"
  254. };
  255.  
  256. // Extras
  257. let nseRenumberTorrentsEnabled = GM_getValue("nseRenumberTorrentsEnabled", true);
  258. let nseHideCategoryIconsEnabled = GM_getValue("nseHideCategoryIconsEnabled", false);
  259. let nseArrowNavigationEnabled = GM_getValue("nseArrowNavigationEnabled", false);
  260.  
  261. // "Fun"
  262. let nseRussianRouletteEnabled = GM_getValue("nseRussianRouletteEnabled", false);
  263. let nseEveryDayIsApril1st = GM_getValue("nseEveryDayIsApril1st", false);
  264.  
  265.  
  266. // +------------------------------+
  267. // | Script setup and preparation |
  268. // +------------------------------+
  269.  
  270. // Figure out what page we're on
  271. let currentPage = "Torrents";
  272. if(window.location.href.includes("top10.php")) {
  273. currentPage = "Top 10";
  274. } else if(window.location.href.includes("user.php?action=notify")) {
  275. currentPage = "Notification filters";
  276. } else if(window.location.href.match(/torrents\.php.*(\?|&)(action=notify)/)) {
  277. currentPage = "Notifications";
  278. } else if(window.location.href.match(/torrents\.php.*(\?|&)(id=)/)) {
  279. currentPage = "Torrent details";
  280. } else if(window.location.href.includes("/collage/")) {
  281. currentPage = "Collage";
  282. } else if(window.location.href.includes("/requests.php")) {
  283. currentPage = "Requests";
  284. } else if(window.location.href.includes("type=uploaded")) {
  285. // Check if we are on our own uploaded page
  286. let myUserID = document.querySelector(".username").href.match(/id=([0-9]+)/)[1];
  287. let pageUserID = window.location.href.match(/userid=([0-9]+)/)[1];
  288.  
  289. if(myUserID === pageUserID) {
  290. currentPage = "My uploaded";
  291. } else {
  292. currentPage = "Uploaded";
  293. }
  294. }
  295.  
  296. // Define which of our pages where filtering doesn't apply, but other functionality should work
  297. let nseUnfilteredPages = ["My uploaded", "Notification filters", "Torrent details"];
  298.  
  299. // Reenable torrent icon box and hide comments if GCD is running, check for its unique elements a.comment
  300. if(currentPage == "Torrents") { //torrents.php is the only page we have in common
  301. window.setTimeout(function() {
  302. if(document.querySelector("a.comment")) {
  303. GM_addStyle(`
  304. span.torrent_icon_container {
  305. display: flex !important;
  306. }
  307. `);
  308.  
  309. for(let i = 0; i < torrents.length; i++) {
  310. let gcdCommentElement = torrents[i].querySelector("td > div > a.comment");
  311. let clmCommentElement = torrents[i].querySelector("td:nth-child(4)");
  312.  
  313. if(gcdCommentElement) {
  314. clmCommentElement.innerHTML = "";
  315. clmCommentElement.appendChild(gcdCommentElement);
  316. }
  317. }
  318. }
  319. }, nseTimeout);
  320. }
  321.  
  322. let nseEnableApril1stOption = false;
  323. if(nseBlacklistTags.includes("hehehehehe")) {
  324. nseEnableApril1stOption = true;
  325. }
  326.  
  327. // Get storage size
  328. window.setTimeout(function() {
  329. let bytesUsed = exportSettings(true);
  330.  
  331. if(bytesUsed < 1024) {
  332. document.getElementById("nseDataUsage").innerHTML = bytesUsed + " bytes";
  333. } else {
  334. document.getElementById("nseDataUsage").innerHTML = Math.round((bytesUsed / 1024) * 100) / 100 + " KiB";
  335. }
  336. }, nseTimeout);
  337.  
  338.  
  339. // Check if we've just updated
  340. if(nseUpdateToastsEnabled) {
  341. if(nseVersionNum > nseSavedVersion) {
  342. GM_setValue("nseSavedVersion", nseVersionNum); // Update saved version so toast is only shown once
  343.  
  344. let updateToast = document.createElement("div");
  345. updateToast.innerHTML = `
  346. <div id="nseUpdateToast">
  347. <div class="nseUpdateToastDiv">
  348. <big>Hooray! NoShitEmpornium was just updated to <b>v${GM_info.script.version}</b>!</big>
  349. </div>
  350.  
  351. <div class="nseUpdateToastDiv">
  352. <span class="nseNiceButton" id="nseSeeWhatsNewButton">
  353. <span class="nseEmoji">📋</span> See what's new!
  354. </span>
  355. <span class="nseNiceButton" id="nseCloseUpdateToastButton">
  356. <span class="nseEmoji">❌</span> Close
  357. </span>
  358. <span class="nseNiceButton" id="nseNeverShowUpdateToastButton">
  359. <span class="nseEmoji">🚫</span> Don't show again
  360. </span>
  361. </div>
  362. </div>
  363. `;
  364.  
  365. let reference = document.querySelector("body > :last-child");
  366. reference.after(updateToast);
  367.  
  368. document.getElementById("nseSeeWhatsNewButton").onclick = function() {
  369. window.open("https://github.com/ceodoe/noshitempornium/blob/master/CHANGELOG.md#latest-changes",'_blank');
  370. this.parentNode.parentNode.remove();
  371. };
  372.  
  373. document.getElementById("nseCloseUpdateToastButton").onclick = function() {
  374. this.parentNode.parentNode.remove();
  375. };
  376.  
  377. document.getElementById("nseNeverShowUpdateToastButton").onclick = function() {
  378. document.getElementById("nseCheckUpdateToasts").checked = false;
  379. saveData();
  380. this.parentNode.parentNode.remove();
  381. };
  382. }
  383. }
  384.  
  385. // +--------------+
  386. // | HTML section |
  387. // +--------------+
  388.  
  389. // Set up reference node
  390. let referenceNode = document.querySelector("div#filter_slidetoggle"); // Torrents
  391.  
  392. if(currentPage == "Top 10" || currentPage == "Notification filters") {
  393. referenceNode = document.querySelector("div.linkbox");
  394. } else if(currentPage == "Notifications") {
  395. referenceNode = document.querySelector("#content > div > h2");
  396. } else if(currentPage == "Torrent details") {
  397. referenceNode = document.querySelector(".linkbox");
  398. } else if(currentPage == "Collage") {
  399. referenceNode = document.querySelector("div.thin > div.clear:nth-child(6)");
  400. } else if(currentPage == "Uploaded" || currentPage == "My uploaded") {
  401. referenceNode = document.querySelector(".submit");
  402. } else if(currentPage == "Requests") {
  403. referenceNode = document.querySelector("#search_form");
  404. }
  405.  
  406. // Die if we can't get a reference node, most likely the site is borked or changed its code
  407. if(!referenceNode) {
  408. throw "Note: NSE died on the way back to his home planet (referenceNode is null)";
  409. }
  410.  
  411. let htmlContent = document.createElement("div");
  412. htmlContent.innerHTML = `
  413. <div id="nseOuter" class="nseOuterDiv">
  414. <div id="nseHeader">
  415. <span id="nseHeaderText">NoShitEmpornium</span>
  416. <span id="nseToggleOptionsNode" class="nseNiceButton">Options</span><span id="nseDynamicRefreshNode" class="nseNiceButton hidden" title="Reload the page to apply your changes" onclick="javascript:location.reload();">🗘</span>
  417. </div>
  418. <div id="nseMainDiv" class="nseMainBox hidden">
  419. <input class="nseRadioButton" id="nseTab1" type="radio" name="tabs" checked>
  420. <label class="nseLabel" for="nseTab1"><span class="nseEmoji">🏷️</span> Tags</label>
  421.  
  422. <input class="nseRadioButton" id="nseTab2" type="radio" name="tabs">
  423. <label class="nseLabel" for="nseTab2"><span class="nseEmoji">📚</span> Titles</label>
  424.  
  425. <input class="nseRadioButton" id="nseTab3" type="radio" name="tabs">
  426. <label class="nseLabel" for="nseTab3"><span class="nseEmoji">👥</span> Uploaders</label>
  427.  
  428. <input class="nseRadioButton" id="nseTab4" type="radio" name="tabs">
  429. <label class="nseLabel" for="nseTab4"><span class="nseEmoji">⚙️</span> Settings</label>
  430.  
  431. <section id="nseContent1">
  432. <div class="nseFieldDiv">
  433. <span class="nseImageButton nseListHeader" id="nseTagBlacklistHeader"><span class="nseEmoji">👎</span> Tag blacklist <small>(space-separated)</small></span><sup class="nseExplanationToggler" id="nseBLEToggler">[?]</sup><br />
  434. <div id="nseBLE" class="nseExplanationBox hidden">
  435. <div class="nseExplanationNode">
  436. <b>TL;DR</b>: <i>If any of these tags exist, hide the torrent</i>
  437. </div>
  438.  
  439. <div class="nseExplanationNode">
  440. This is where you specify tags you don't want to see. Any torrent matching
  441. any of these tags will be hidden unless overridden by any of the whitelist
  442. rules. The correct format for this field is the same used for tags on
  443. Empornium itself: Tags are separated by spaces, and uses a period between
  444. words within a tag. Character case does not matter. Blacklisted tags will be
  445. <span class="nseHiddenTag">highlighted</span> when viewing
  446. hidden torrents.
  447. </div>
  448.  
  449. <div class="nseExplanationNode">
  450. Example:<br />
  451. <pre>scat piss.drinking jerk.off.instruction non.nude</pre>
  452. </div>
  453. </div>
  454. <textarea class="nseTextArea" id="nseBlacklistTaglistArea" rows=10>${nseBlacklistTaglist}</textarea>
  455. </div>
  456. <div class="nseFieldDiv${nseHardPassEnabled ? '' : ' hidden'}">
  457. <span class="nseImageButton nseListHeader" id="nseTagHardPassHeader"><span class="nseEmoji">🚫</span> Hard Pass blacklist <small>(space-separated)</small></span><sup class="nseExplanationToggler" id="nseHPEToggler">[?]</sup><br />
  458. <div id="nseHPE" class="nseExplanationBox hidden">
  459. <div class="nseExplanationNode">
  460. <b>TL;DR</b>: <i>If any of these tags exist, hide the torrent <b>no matter what</b></i>
  461. </div>
  462.  
  463. <div class="nseExplanationNode">
  464. This is where you specify tags you don't want to see at all. Any torrent
  465. matching any of these tags will be hidden unless overridden by individual
  466. filtering (clicking the eye icon). The correct format for this field is the
  467. same used for tags on Empornium itself: Tags are separated by spaces, and
  468. uses a period between words within a tag. Character case does not matter.
  469. Hard Pass tags will be <span class="nseHardPassTag">highlighted</span> when viewing hidden torrents. The torrent will be removed completely from the results if the Black Hole
  470. option is enabled.
  471. </div>
  472.  
  473. <div class="nseExplanationNode">
  474. Example:<br />
  475. <pre>virtual.reality scat puke blood enema</pre>
  476. </div>
  477. </div>
  478. <textarea class="nseTextArea" id="nseHardPassTaglistArea" rows=10>${nseHardPassTaglist}</textarea>
  479. </div>
  480. <div class="nseFieldDiv">
  481. <span class="nseImageButton nseListHeader" id="nseTagWhitelistHeader"><span class="nseEmoji">👍</span> Tag whitelist <small>(space-separated)</small></span><sup class="nseExplanationToggler" id="nseWLEToggler">[?]</sup><br />
  482. <div id="nseWLE" class="nseExplanationBox hidden">
  483. <div class="nseExplanationNode">
  484. <b>TL;DR</b>: <i>If any of these tags exist, ignore all other rules and show the torrent</i>
  485. </div>
  486.  
  487. <div class="nseExplanationNode">
  488. This is where you specify tags you want to show regardless of all other
  489. rules. In other words, no matter if the torrent matches on tags, title or
  490. uploader blacklists &mdash; if it contains any of these whitelisted tags, it
  491. will be shown regardless. The correct format for this field is the same used
  492. for tags on Empornium itself: Tags are separated by spaces, and uses a
  493. period between words within a tag. Character case does not matter.
  494. Whitelisted tags will be <span class="nseWhitelistedTag">highlighted</span>.
  495. </div>
  496.  
  497. <div class="nseExplanationNode">
  498. Example:<br />
  499. <pre>sasha.grey huge.ass gianna.michaels femdom</pre>
  500. </div>
  501. </div>
  502. <textarea class="nseTextArea" id="nseWhitelistTaglistArea" rows=10>${nseWhitelistTaglist}</textarea>
  503. </div>
  504. </section>
  505.  
  506. <section id="nseContent2">
  507. <div class="nseFieldDiv">
  508. <span class="nseImageButton nseListHeader" id="nseTitleBlacklistHeader"><span class="nseEmoji">👎</span> Title blacklist <small>(semicolon-separated)</small></span><sup class="nseExplanationToggler" id="nseTitleBLEToggler">[?]</sup><br />
  509. <div id="nseTitleBLE" class="nseExplanationBox hidden">
  510. <div class="nseExplanationNode">
  511. <b>TL;DR</b>: <i>If any of these phrases are in the title, hide the torrent</i>
  512. </div>
  513.  
  514. <div class="nseExplanationNode">
  515. This is where you specify title phrases you want to filter on. This is
  516. useful for hiding untagged content with a recurring theme (for example
  517. specific JAV series you don't care about, or re-encoded content).
  518. Character case does not matter. <b>Title phrases are separated by
  519. semicolons <span class="nseMonospace">;</span> &mdash; not spaces, unlike
  520. tags or uploaders!</b>
  521. </div>
  522.  
  523. <div class="nseExplanationNode">
  524. Example:<br />
  525. <pre>sdmm;hikr;princess peach;reencode</pre>
  526. </div>
  527. </div>
  528. <textarea class="nseTextArea" id="nseBlacklistTitleListArea" rows=10>${nseBlacklistTitleList}</textarea>
  529. </div>
  530. <div class="nseFieldDiv">
  531. <span class="nseImageButton nseListHeader" id="nseTitleWhitelistHeader"><span class="nseEmoji">👍</span> Title whitelist <small>(semicolon-separated)</small></span><sup class="nseExplanationToggler" id="nseTitleWLEToggler">[?]</sup><br />
  532. <div id="nseTitleWLE" class="nseExplanationBox hidden">
  533. <div class="nseExplanationNode">
  534. <b>TL;DR</b>: <i>If any of these phrases are in the title, ignore all other rules and show the torrent</i>
  535. </div>
  536.  
  537. <div class="nseExplanationNode">This is where you specify title phrases you want to show regardless of other rules. Character case does not matter. <b>Title phrases are separated by semicolons <span class="nseMonospace">;</span> &mdash; not spaces like tags or uploaders!</b>
  538. </div>
  539.  
  540. <div class="nseExplanationNode">Example:<br /><pre>minipack;super mario;sdmm;moist</pre></div>
  541. </div>
  542. <textarea class="nseTextArea" id="nseWhitelistTitleListArea" rows=10>${nseWhitelistTitleList}</textarea>
  543. </div>
  544. </section>
  545.  
  546. <section id="nseContent3">
  547. <div class="nseFieldDiv">
  548. <span class="nseImageButton nseListHeader" id="nseUploaderBlacklistHeader"><span class="nseEmoji">👎</span> Uploader blacklist <small>(space-separated)</small></span><sup class="nseExplanationToggler" id="nseUBLEToggler">[?]</sup><br />
  549. <div id="nseUBLE" class="nseExplanationBox hidden">
  550. <div class="nseExplanationNode">
  551. <b>TL;DR</b>: <i>If this torrent is uploaded by any of these users, hide it</i>
  552. </div>
  553.  
  554. <div class="nseExplanationNode">
  555. This is where you specify the names of uploaders you want to hide all
  556. uploads from, unless overriden by a whitelist rule. Uploader names will be
  557. <span class="nseHiddenUploader">highlighted</span> when viewing
  558. hidden torrents. Character case does not matter. Note that filtering based
  559. on usernames will not function on collage or user upload pages, as torrent
  560. uploaders are not exposed on those pages.
  561. </div>
  562.  
  563. <div class="nseExplanationNode">
  564. Example:<br />
  565. <pre>SuperUploader2007 LowQualityUploadsIncorporated</pre>
  566. </div>
  567. </div>
  568. <textarea class="nseTextArea" id="nseBlacklistUploadersArea" rows=10>${nseBlacklistUploadersList}</textarea>
  569. </div>
  570. <div class="nseFieldDiv">
  571. <span class="nseImageButton nseListHeader" id="nseUploaderWhitelistHeader"><span class="nseEmoji">👍</span> Uploader whitelist <small>(space-separated)</small></span><sup class="nseExplanationToggler" id="nseUWLEToggler">[?]</sup><br />
  572. <div id="nseUWLE" class="nseExplanationBox hidden">
  573. <div class="nseExplanationNode">
  574. <b>TL;DR</b>: <i>If this torrent is uploaded by any of these users, show it regardless of any other rules</i>
  575. </div>
  576.  
  577. <div class="nseExplanationNode">
  578. This is where you specify the names of uploaders you want to show all
  579. uploads from, regardless of any blacklist rules. Uploader names will be
  580. <span class="nseWhitelistedUploader">highlighted</span>. Character
  581. case does not matter. Note that filtering based on usernames will not
  582. function on collage or user upload pages, as torrent uploaders are not
  583. exposed on those pages.
  584. </div>
  585.  
  586. <div class="nseExplanationNode">
  587. Example:<br />
  588. <pre>NobelPrizeWinningUploads ActuallyGreatUploader69 ceodoe</pre>
  589. </div>
  590. </div>
  591. <textarea class="nseTextArea" id="nseWhitelistUploadersArea" rows=10>${nseWhitelistUploadersList}</textarea>
  592. </div>
  593. </section>
  594.  
  595. <section id="nseContent4">
  596. <input class="nseRadioButton" id="nseSettingsTab1" type="radio" name="settingsTabs" checked>
  597. <label class="nseLabel" for="nseSettingsTab1"><span class="nseEmoji">🔍</span> Filtering</label>
  598.  
  599. <input class="nseRadioButton" id="nseSettingsTab2" type="radio" name="settingsTabs">
  600. <label class="nseLabel" for="nseSettingsTab2"><span class="nseEmoji">🖥️</span> Interface</label>
  601.  
  602. <input class="nseRadioButton" id="nseSettingsTab3" type="radio" name="settingsTabs">
  603. <label class="nseLabel" for="nseSettingsTab3"><span class="nseEmoji">🗃️</span> Data management</label>
  604.  
  605. <input class="nseRadioButton" id="nseSettingsTab4" type="radio" name="settingsTabs">
  606. <label class="nseLabel" for="nseSettingsTab4"><span class="nseEmoji">ℹ️</span> About</label>
  607.  
  608. <section id="nseSettingsContent1">
  609. <h3 class="nseH3">Filtering</h3>
  610. <b>Individual uploads</b><br />
  611. <input type="checkbox" id="nseCheckIndividualHide"${nseIndividualUploadHidingEnabled ? ' checked' : ''} />
  612. <label for="nseCheckIndividualHide" class="nseSettingsCheckbox">
  613. <span class="nseEmoji">👁️</span> Enable individual upload filtering
  614. </label><br />
  615. <span class="nseExplanationSpan nseESOffset">(Click the eye icon next to the torrent name to blacklist/whitelist uploads</span><br />
  616. <span class="nseExplanationSpan nseESOffset">individually, ignoring <b>all</b> other rules. These filters are automatically saved)</span><br /><br />
  617.  
  618. <input type="checkbox" id="nseCheckFilterAllButtonEnabled"${nseFilterAllButtonEnabled ? ' checked' : ''} />
  619. <label for="nseCheckFilterAllButtonEnabled" class="nseSettingsCheckbox">
  620. <span class="nseEmoji">👁️</span> Enable "Filter all unfiltered results" button
  621. </label><br />
  622. <span class="nseExplanationSpan nseESOffset">(Requires individual upload filtering to be enabled to function)</span>
  623. <br /><br />
  624.  
  625.  
  626. <b>List management</b><br />
  627. <input type="checkbox" id="nseCheckRightClickManagementEnabled"${nseRightClickManagementEnabled ? ' checked' : ''} />
  628. <label for="nseCheckRightClickManagementEnabled" class="nseSettingsCheckbox">
  629. <span class="nseEmoji">🖱️</span> Enable Right-Click Management (RCM)
  630. </label>
  631. <div id="nseRCMCustomizationDiv" ${nseRightClickManagementEnabled ? '' : 'class="hidden"'}>
  632. <span class="nseESOffset">
  633. <input type="checkbox" id="nseCheckRCMTagsEnabled"${nseRCMTagsEnabled ? ' checked' : ''} />
  634. <label for="nseCheckRCMTagsEnabled" class="nseSettingsCheckbox">
  635. <span class="nseEmoji">🏷️</span> Enable for tags
  636. </label><br />
  637. </span>
  638. <span class="nseESOffset">
  639. <input type="checkbox" id="nseCheckRCMTitlesEnabled"${nseRCMTitlesEnabled ? ' checked' : ''} />
  640. <label for="nseCheckRCMTitlesEnabled" class="nseSettingsCheckbox">
  641. <span class="nseEmoji">📚</span> Enable for titles
  642. </label><br />
  643. </span>
  644. <span class="nseESOffset">
  645. <input type="checkbox" id="nseCheckRCMUploadersEnabled"${nseRCMUploadersEnabled ? ' checked' : ''} />
  646. <label for="nseCheckRCMUploadersEnabled" class="nseSettingsCheckbox">
  647. <span class="nseEmoji">👥</span> Enable for uploaders
  648. </label>
  649. </span>
  650. </div><br />
  651.  
  652. <span class="nseExplanationSpan">Right-click a tag/title/uploader in the torrent list to add/remove from your lists.</span><br /><br />
  653. <b>Torrent site status</b><br />
  654. <input type="checkbox" id="nseCheckHideAnonUploads"${nseHideAnonUploadsEnabled ? ' checked' : ''} />
  655. <label for="nseCheckHideAnonUploads" class="nseSettingsCheckbox">
  656. <span class="nseEmoji">👤</span> Hide all anonymous uploads
  657. </label>
  658. <span class="nseExplanationSpan">(Will still be overridden by whitelist rules)</span><br />
  659.  
  660. <input type="checkbox" id="nseCheckHideReported"${nseHideReportedEnabled ? ' checked' : ''} />
  661. <label for="nseCheckHideReported" class="nseSettingsCheckbox">
  662. <span class="nseEmoji">⛔</span> Hide reported uploads
  663. </label>
  664. <span class="nseExplanationSpan">(Hide torrents with active reports, whitelists will override)</span><br />
  665.  
  666. <input type="checkbox" id="nseCheckHideWarned"${nseHideWarnedEnabled ? ' checked' : ''} />
  667. <label for="nseCheckHideWarned" class="nseSettingsCheckbox">
  668. <span class="nseEmoji">⚔️</span> Hide warned uploads
  669. </label>
  670. <span class="nseExplanationSpan">(Hide torrents with active warning, whitelists will override)</span><br />
  671.  
  672. <input type="checkbox" id="nseCheckHideUnseeded"${nseHideUnseededEnabled ? ' checked' : ''} />
  673. <label for="nseCheckHideUnseeded" class="nseSettingsCheckbox">
  674. <span class="nseEmoji">🏜️</span> Hide unseeded uploads
  675. </label>
  676. <span class="nseExplanationSpan">(Hide torrents with zero seeders, whitelists will override)</span><br /><br />
  677.  
  678. <b>Torrent personal status</b><br />
  679. <input type="checkbox" id="nseCheckHideGrabbed"${nseHideGrabbedEnabled ? ' checked' : ''} />
  680. <label for="nseCheckHideGrabbed" class="nseSettingsCheckbox">
  681. <span class="nseEmoji">💾</span> Hide grabbed uploads
  682. </label>
  683. <span class="nseExplanationSpan">(Hide torrents you have previously grabbed)</span><br />
  684.  
  685. <input type="checkbox" id="nseCheckHideLeeching"${nseHideLeechingEnabled ? ' checked' : ''} />
  686. <label for="nseCheckHideLeeching" class="nseSettingsCheckbox">
  687. <span class="nseEmoji">⏬</span> Hide leeching uploads
  688. </label>
  689. <span class="nseExplanationSpan">(Hide torrents you are currently leeching)</span><br />
  690.  
  691. <input type="checkbox" id="nseCheckHideSeeding"${nseHideSeedingEnabled ? ' checked' : ''} />
  692. <label for="nseCheckHideSeeding" class="nseSettingsCheckbox">
  693. <span class="nseEmoji">⏫</span> Hide seeding uploads
  694. </label>
  695. <span class="nseExplanationSpan">(Hide torrents you are currently seeding)</span><br />
  696.  
  697. <input type="checkbox" id="nseCheckHideSnatched"${nseHideSnatchedEnabled ? ' checked' : ''} />
  698. <label for="nseCheckHideSnatched" class="nseSettingsCheckbox">
  699. <span class="nseEmoji">💽</span> Hide snatched uploads
  700. </label>
  701. <span class="nseExplanationSpan">(Hide torrents you have already downloaded/snatched)</span><br />
  702.  
  703. <input type="checkbox" id="nseCheckHideBookmarked"${nseHideBookmarkedEnabled ? ' checked' : ''} />
  704. <label for="nseCheckHideBookmarked" class="nseSettingsCheckbox">
  705. <span class="nseEmoji">⭐</span> Hide bookmarked uploads
  706. </label>
  707. <span class="nseExplanationSpan">(Hide torrents you have previously bookmarked)</span><br /><br />
  708.  
  709. <input type="checkbox" id="nseCheckBypassWhitelists"${nseBypassWhitelistsEnabled ? ' checked' : ''} />
  710. <label for="nseCheckBypassWhitelists" class="nseSettingsCheckbox">
  711. <span class="nseEmoji">💫</span> Bypass whitelists for personal status filtering
  712. </label><br />
  713. <span class="nseExplanationSpan nseESOffset">(Ignore all whitelists when filtering grabbed/leeching/seeding/snatched torrents)</span><br /><br />
  714.  
  715. <b>Hard Pass</b><br />
  716. <input type="checkbox" id="nseCheckHardPassEnabled"${nseHardPassEnabled ? ' checked' : ''} />
  717. <label for="nseCheckHardPassEnabled" class="nseSettingsCheckbox">
  718. <span class="nseEmoji">🚫</span> Hard Pass
  719. </label><span class="nseExplanationSpan">(Enable the Hard Pass blacklist, found in the "Tags" tab)</span><br />
  720. <input type="checkbox" id="nseCheckRemoveHardPassResults"${nseRemoveHardPassResults ? ' checked' : ''} />
  721. <label for="nseCheckRemoveHardPassResults" class="nseSettingsCheckbox">
  722. <span class="nseEmoji">⚫</span> Black Hole
  723. </label><span class="nseExplanationSpan">(Remove results instead of hiding them behind the toggle)</span><br /><br />
  724. <span class="nseExplanationSpan">Hard Pass enables an extra tag blacklist that will make sure that torrents with the given tags will be hidden no matter what. The only thing that can override Hard Pass is whitelisting a torrent through individual filtering when Black Hole is disabled. Enable the Black Hole option to remove results from the page entirely.</span><br /><br />
  725. </section>
  726.  
  727. <section id="nseSettingsContent2">
  728. <h3 class="nseH3">Interface</h3>
  729.  
  730. <div>
  731. <input type="checkbox" id="nseCheckObliviousMode"${nseObliviousModeEnabled ? ' checked' : ''} />
  732. <label for="nseCheckObliviousMode" class="nseSettingsCheckbox">
  733. <span class="nseEmoji">❓</span> Oblivious
  734. </label>
  735. <span class="nseExplanationSpan">(Hide torrent tag lists)</span><br />
  736.  
  737. <input type="checkbox" id="nseCheckCustomCSS"${nseCustomCSSEnabled ? ' checked' : ''} />
  738. <label for="nseCheckCustomCSS" class="nseSettingsCheckbox">
  739. <span class="nseEmoji">📜</span> Custom CSS
  740. </label>
  741. <span class="nseExplanationSpan">(Define your own CSS rules)</span><br />
  742. <div id="nseCustomCSSDiv" ${nseCustomCSSEnabled ? '' : 'class="hidden"'}>
  743. Define your custom CSS below. Note that this code is injected at the very end of the built-in CSS, so use the !important tag liberally to overwrite existing rules. Do not escape backslashes, it will be done automatically. Avoid using backticks. <br />
  744. <textarea class="nseTextArea" id="nseCustomCSSArea" rows=10>${nseCustomCSS.replace(/\\\\/gi,"\\")}</textarea>
  745. </div>
  746.  
  747. <input type="checkbox" id="nseCheckScrollToNSEEnabled"${nseScrollToNSEEnabled ? ' checked' : ''} />
  748. <label for="nseCheckScrollToNSEEnabled" class="nseSettingsCheckbox">
  749. <span class="nseEmoji">⚓</span> Automatically scroll to NSE on page load
  750. </label>
  751. <span class="nseExplanationSpan">(Makes browsing several pages easier)</span><br />
  752.  
  753. <input type="checkbox" id="nseCheckEmojiEnabled"${nseEmojiEnabled ? ' checked' : ''} />
  754. <label for="nseCheckEmojiEnabled" class="nseSettingsCheckbox">
  755. <span class="nseEmoji">🖼️</span> Enable extended Unicode icons ("emoji")
  756. </label>
  757. <span class="nseExplanationSpan">(Disable if you see garbled characters)</span><br />
  758. <input type="checkbox" id="nseCheckUpdateToasts"${nseUpdateToastsEnabled ? ' checked' : ''} />
  759. <label for="nseCheckUpdateToasts" class="nseSettingsCheckbox">
  760. <span class="nseEmoji">❗</span> Notify me when NSE is updated
  761. </label>
  762. <span class="nseExplanationSpan">(Get changelog info on update)</span><br />
  763.  
  764. <input type="checkbox" id="nseCheckFloatingToggleButton"${nseFloatingToggleButtonEnabled ? ' checked' : ''} />
  765. <label for="nseCheckFloatingToggleButton" class="nseSettingsCheckbox">
  766. <span class="nseEmoji">🛟</span> Enable the floating toggle button on the bottom right of the page
  767. </label><br />
  768.  
  769. <input type="checkbox" id="nseCheckOpenAllButton"${nseOpenAllButtonEnabled ? ' checked' : ''} />
  770. <label for="nseCheckOpenAllButton" class="nseSettingsCheckbox">
  771. <span class="nseEmoji">📂</span> Enable the "Open all unfiltered results" button
  772. </label><br />
  773. <span class="nseExplanationSpan nseESOffset">(Appears under the NSE options area. If your browser tells you it blocked Emp</span><br />
  774. <span class="nseExplanationSpan nseESOffset">from opening popups, click the Preferences button and allow popups for Emp)</span><br />
  775. <input type="checkbox" id="nseCheckOpenAllGoNext"${nseOpenAllGoNextEnabled ? ' checked' : ''} />
  776. <label for="nseCheckOpenAllGoNext" class="nseSettingsCheckbox">
  777. <span class="nseEmoji">➡️</span> Automatically go to next results page after clicking "Open all" button
  778. </label><br /><br />
  779.  
  780. <br />
  781.  
  782. User interface font family: <br />
  783. <input type="text" class="nseInput" value="${nseUIFont}" id="nseUIFont" /> <span class="nseExplanationSpan">(Corresponds to the <a class="nseLink" href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-family" target="_blank"><b><u>CSS font-family property</u></b></a>)</span>
  784. <br /><br />
  785.  
  786. Filter list font family: <br />
  787. <input type="text" class="nseInput" value="${nseTextAreaFont}" id="nseTextAreaFont" /> <span class="nseExplanationSpan">(Font for black/whitelists textareas)</span>
  788. <br /><br />
  789.  
  790. Highlight color for blacklisted elements: <br />
  791. <input type="text" class="nseInput" value="${nseBlacklistColor}" id="nseBlacklistColor" /> <span class="nseExplanationSpan">(You can use any <a class="nseLink" href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value" target="_blank"><b><u>CSS color notation</u></b></a> here)</span>
  792. <br /><br />
  793. Highlight color for Hard Pass tags: <br />
  794. <input type="text" class="nseInput" value="${nseHardPassColor}" id="nseHardPassColor" />
  795. <br /><br />
  796. Highlight color for whitelisted elements: <br />
  797. <input type="text" class="nseInput" value="${nseWhitelistColor}" id="nseWhitelistColor" />
  798. <br /><br />
  799.  
  800. Timeout for timed functions (milliseconds): <br />
  801. <input type="text" class="nseInput" style="width: 50px;" value="${nseTimeout}" id="nseTimeout" /> <span class="nseExplanationSpan">(Used for GCD support and computing storage size)</span>
  802. <br /><br />
  803.  
  804.  
  805. Theme:<br />
  806. <select name="nseThemeDropdown" id="nseThemeDropdown" class="nseInput">
  807. <option value="nseThemeDefault" ${nseSelectedTheme=="nseThemeDefault" ? "selected='selected'" : ''}>Default</option>
  808. <option value="nseThemeLegacy" ${nseSelectedTheme=="nseThemeLegacy" ? "selected='selected'" : ''}>Legacy</option>
  809. <option value="nseThemeEdgy" ${nseSelectedTheme=="nseThemeEdgy" ? "selected='selected'" : ''}>Edgy</option>
  810. <option value="nseThemeBaked" ${nseSelectedTheme=="nseThemeBaked" ? "selected='selected'" : ''}>Baked</option>
  811. <option value="nseThemeDarkPurple" ${nseSelectedTheme=="nseThemeDarkPurple" ? "selected='selected'" : ''}>Dark Purple</option>
  812. <option value="nseThemeCustom" ${nseSelectedTheme=="nseThemeCustom" ? "selected='selected'" : ''}>Custom</option>
  813. </select>
  814. <span id="nseThemeDescription" class="nseExplanationSpan">${nseThemeDescriptions[nseSelectedTheme]}</span>
  815.  
  816. <div id="nseCustomThemeDiv" ${nseSelectedTheme=="nseThemeCustom" ? '' : 'class="hidden"'}>
  817. <p>
  818. You can use any <a class="nseLink" href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value" target="_blank">CSS color notation</a> here.<br />
  819. Examples: <span class="nseMonospace">#ffffff &mdash; rgb(0,0,255) &mdash; aquamarine</span>
  820. </p>
  821. <p>
  822. Background color:<br />
  823. <input type="text" class="nseInput" id="nseCustomThemeBgCol" value='${nseCustomTheme.backgroundColor}' />
  824. </p>
  825. <p>
  826. Background highlight color:<br />
  827. <input type="text" class="nseInput" id="nseCustomThemeBgHighCol" value='${nseCustomTheme.backgroundHighlightColor}' />
  828. </p>
  829. <p>
  830. Foreground color:<br />
  831. <input type="text" class="nseInput" id="nseCustomThemeForeCol" value='${nseCustomTheme.foregroundColor}' />
  832. </p>
  833. <p>
  834. Accent color:<br />
  835. <input type="text" class="nseInput" id="nseCustomThemeAccentCol" value='${nseCustomTheme.accentColor}' />
  836. </p>
  837. <p>
  838. Highlight color:<br />
  839. <input type="text" class="nseInput" id="nseCustomThemeHighCol" value='${nseCustomTheme.highlightColor}' />
  840. </p>
  841. <p>
  842. Hidden torrent background color:<br />
  843. <input type="text" class="nseInput" id="nseCustomThemeHiddenBgCol" value='${nseCustomTheme.hiddenBackgroundColor}' />
  844. </p>
  845.  
  846. <p>Remember to click the [Save] button to save your changes!</p>
  847. </div>
  848. </div>
  849.  
  850. <div class="nseTopAiryDiv">
  851. <b>Extras</b><br />
  852. <input type="checkbox" id="nseCheckRenumberTorrents"${nseRenumberTorrentsEnabled ? ' checked' : ''} />
  853. <label for="nseCheckRenumberTorrents" class="nseSettingsCheckbox">
  854. <span class="nseEmoji">🔢</span> Renumber torrents on "Top X" pages
  855. </label>
  856. <span class="nseExplanationSpan">(Fixes torrent numbering after filtering)</span><br />
  857. <input type="checkbox" id="nseCheckHideCategoryIcons"${nseHideCategoryIconsEnabled ? ' checked' : ''} />
  858. <label for="nseCheckHideCategoryIcons" class="nseSettingsCheckbox">
  859. <span class="nseEmoji">🟪</span> Hide category icons
  860. </label>
  861. <span class="nseExplanationSpan">(Hides category icons/links in torrent lists)</span><br />
  862. <input type="checkbox" id="nseCheckArrowNavigation"${nseArrowNavigationEnabled ? ' checked' : ''} />
  863. <label for="nseCheckArrowNavigation" class="nseSettingsCheckbox">
  864. <span class="nseEmoji">⌨️</span> Navigate paginated torrent lists with keyboard arrow keys
  865. </label><br />
  866. <span class="nseExplanationSpan nseESOffset">(Note that NSE only runs on torrent lists, if you want</span><br />
  867. <span class="nseExplanationSpan nseESOffset">arrow key navigation globally on Empornium, use <a class="nseLink" href="https://github.com/ceodoe/noshitempornium/raw/master/GlobalArrowKeyNavigation.user.js" target="_blank"><b><u>this script</u></b></a>)</span>
  868. </div>
  869.  
  870. <div class="nseTopAiryDiv nseBtmAiryDiv">
  871. <b>"Fun"</b><br />
  872. <input type="checkbox" id="nseCheckRussianRouletteMode"${nseRussianRouletteEnabled ? ' checked' : ''} />
  873. <label for="nseCheckRussianRouletteMode" class="nseSettingsCheckbox">
  874. <span class="nseEmoji">🎲</span> Russian Roulette
  875. </label>
  876. <span class="nseExplanationSpan">(Randomly and silently show filtered torrents)</span><br />
  877.  
  878. ${nseEnableApril1stOption ? `<input type="checkbox" id="nseCheckApril1stAllYear"${nseEveryDayIsApril1st ? ' checked' : ''} />
  879. <label for="nseCheckApril1stAllYear" class="nseSettingsCheckbox">
  880. <span class="nseEmoji">🤪</span> Every day is April 1st
  881. </label>
  882. <span class="nseExplanationSpan">(For the masochists among us)</span>` : ''}
  883. </div>
  884. </section>
  885.  
  886. <section id="nseSettingsContent3">
  887. <h3 class="nseH3">Data management</h3>
  888. <p>
  889. <span class="nseExplanationSpan">Use these functions to import, export or reset all your NSE lists and settings.</span><br /><br />
  890.  
  891. NSE is currently using <span id="nseDataUsage"></span> of data in your LocalStorage.
  892.  
  893. <div class="nseNiceBox"><span class="nseEmoji">⤵️</span> Import NSE data<br />
  894. <input type="file" accept=".json,text/plain" id="nseImportFilePicker" class="nseInput">
  895. </div><br />
  896. <span class="nseNiceButton" id="nseExportButton"><span class="nseEmoji">⤴️</span> Export NSE data</span>
  897. <span class="nseNiceButton" id="nseEraseDataButton"><span class="nseEmoji">🔄</span> Reset NSE data</span><br /><br />
  898. <span class="nseExplanationSpan"><b>IMPORTANT: Data overwritten or reset by the functions above is <i>not</i> recoverable!</b></span>
  899. </p>
  900. </section>
  901.  
  902. <section id="nseSettingsContent4">
  903. <h3 class="nseH3">About</h3>
  904. <p>
  905. Copyright &copy; 2015-2022 ceodoe. NoShitEmpornium v${GM_info.script.version} was made with ${nseEmojiEnabled ? '💕' : 'love'} by <a class="nseLink" href="/user.php?id=508194">ceodoe</a> of Empornium, and its code is licensed under the <a class="nseLink" href="https://www.gnu.org/licenses/gpl-3.0.txt" target="_blank">GNU General Public License v3.0</a>.
  906. </p>
  907.  
  908. <h3 class="nseH3">Statistics</h3>
  909. <p>
  910. <span class="nseExplanationSpan">This section is populated when the page loads, and does not reflect changes done thereafter.</span><br />
  911. <br />
  912. Number of blacklisted tags: <span class="nseHiddenTag">${nseBlacklistTags.length}</span><br />
  913. ${nseHardPassEnabled ? `Number of Hard Pass tags: <span class="nseHiddenTag">${nseHardPassTags.length}</span><br />` : ""}
  914. Number of blacklisted title phrases: <span class="nseHiddenTag">${nseBlacklistTitlePhrases.length}</span><br />
  915. Number of blacklisted uploaders: <span class="nseHiddenTag">${nseBlacklistUploaders.length}</span><br />
  916. ${nseIndividualUploadHidingEnabled ? `Number of individually blacklisted torrents: <span class="nseHiddenTag">${nseIndividualUploadHidingBlacklist.length}</span><br />` : ""}
  917. <br />
  918. Number of whitelisted tags: <span class="nseWhitelistedTag">${nseWhitelistTags.length}</span><br />
  919. Number of whitelisted title phrases: <span class="nseWhitelistedTag">${nseWhitelistTitlePhrases.length}</span><br />
  920. Number of whitelisted uploaders: <span class="nseWhitelistedTag">${nseWhitelistUploaders.length}</span><br />
  921. ${nseIndividualUploadHidingEnabled ? `Number of individually whitelisted torrents: <span class="nseWhitelistedTag">${nseIndividualUploadHidingWhitelist.length}</span><br />` : ""}
  922. </p>
  923.  
  924. <h3 class="nseH3">Resources</h3>
  925. <p>
  926. <b>
  927. <span class="nseEmoji">🧵</span> <a class="nseLink" href="/forum/thread/44258?postid=956045#post956045" target="_blank">Read the official thread on the Empornium forums</a><br />
  928.  
  929. <span class="nseEmoji">🐙</span> <a class="nseLink" href="https://github.com/ceodoe/noshitempornium" target="_blank">Report a bug or view commit history on GitHub</a><br />
  930. <span class="nseEmoji">📋</span> <a class="nseLink" href="https://github.com/ceodoe/noshitempornium/blob/master/CHANGELOG.md#latest-changes" target="_blank">Read the changelog</a><br />
  931. </b>
  932. </p>
  933.  
  934. <h3 class="nseH3">Contribute</h3>
  935. <p>
  936. If you love NSE, consider a donation! I've spent a lot of time making this, and any sum is hugely appreciated. Credits on the site is also appreciated.<br /> <br />
  937.  
  938. Bitcoin (BTC): <span class="nseMonospace"><small>15YM9XCe5isbf1T8TBjku1BYmyNTj1ZyX9</small></span><br />
  939. Bitcoin Cash (BCH): <span class="nseMonospace"><small>qqcuude6a6q09r0a6ujvvklzm6qjrxtr5vfwkxrk34</small></span><br />
  940. Ethereum (ETH): <span class="nseMonospace"><small>0xD66eB1CafE88f299929b4FCedCCac3B3D9d7Bee1</small></span><br />
  941. Stellar (XLM): <span class="nseMonospace"><small>GD7KXIYO3FTINLBXIJCBMOXVMO4DCXNSLFJTEAIILZAODEMDQRSOT532</small></span>
  942. <br /><br />
  943.  
  944. You can also contribute by reporting bugs, submitting feature requests, pull requests, or just generally using NSE to its fullest. Thanks for using my software!
  945. </p>
  946.  
  947. <p></p>
  948. </section>
  949. </section>
  950.  
  951. <div class="nseSettingsControlDiv">
  952. <span id="nseSaveButton" class="nseNiceButton"><span class="nseEmoji">💾</span> Save</span>
  953. <span id="nseReloadButton" class="nseNiceButton"><span class="nseEmoji">🔃</span> Reload page and apply changes</span>
  954. <span id="nseCloseOptionsButton" class="nseNiceButton"><span class="nseEmoji">❌</span> Close options</span>
  955. </div>
  956.  
  957. <div class="nseSettingsControlDiv" id="nseSaveDiv" class="hidden">
  958.  
  959. </div>
  960. </div>
  961. </div>
  962.  
  963. ${!nseUnfilteredPages.includes(currentPage) && (nseOpenAllButtonEnabled || (nseFilterAllButtonEnabled && nseIndividualUploadHidingEnabled)) ? `
  964. <div class="nseTopAiryDiv nseBtmAiryDiv nseCenterDiv">
  965. ${nseOpenAllButtonEnabled ? `
  966. <div style="display: inline;">
  967. <span class="nseNiceButton" id="nseOpenAllButton">
  968. <span class="nseEmoji">📂</span>
  969. Open all unfiltered results
  970. </span>
  971. </div>
  972. ` : ''}
  973.  
  974. ${nseFilterAllButtonEnabled && nseIndividualUploadHidingEnabled ? `
  975. <div style="display: inline;">
  976. <span class="nseNiceButton" id="nseFilterAllButton">
  977. <span class="nseEmoji">👁</span>
  978. Filter all unfiltered results
  979. </span>
  980. </div>
  981. ` : ''}
  982. </div>
  983. ` : ''}
  984.  
  985.  
  986. <div id="nseRCMBox" class="hidden">
  987. <div id="nseRCMClose">${nseEmojiEnabled ? '❌' : '<span class="nseHiddenTag"><b><big>X</big></b></span>'}</div>
  988. <p id="nseRCMBoxInfoText">Tag/uploader placeholder text</p>
  989. <p id="nseRCMBoxChoices">
  990.  
  991. </p>
  992. <p><small>Your settings will be automatically saved when you choose one of the above options. Your changes will reflect in the torrent list once the page is <span class="nseSpanLink" onclick="location.reload();"><b>reloaded</b></a>.</small></p>
  993. </div>
  994. `;
  995.  
  996. referenceNode.after(htmlContent);
  997.  
  998. // Do this before filtering in case it takes a while to filter the page
  999. if(nseScrollToNSEEnabled) {
  1000. document.getElementById("nseOuter").scrollIntoView();
  1001. }
  1002.  
  1003. // Add floating toggle button if enabled
  1004. if(nseFloatingToggleButtonEnabled) {
  1005. document.querySelector("body").insertAdjacentHTML("afterbegin", `
  1006. <div id="nseFloatyBoi">
  1007. <span class="nseEmoji" alt="Toggle hidden torrents" title="Toggle hidden torrents">👁️</span>
  1008. </div>
  1009. `);
  1010. document.querySelector("#nseFloatyBoi").addEventListener("click", function() { toggleTorrents(); });
  1011. }
  1012.  
  1013. // Set up elements for "Notification filters" page
  1014. if(currentPage == "Notification filters") {
  1015. let textareas = document.querySelectorAll("textarea.long");
  1016.  
  1017. for(let i = 0; i < textareas.length; i++) {
  1018. let newElement = document.createElement("p");
  1019. newElement.classList.add("min_padding");
  1020. newElement.style.marginTop = "10px";
  1021. if(textareas[i].name == "tags") {
  1022. newElement.innerHTML = `
  1023. <span class="nseNiceButton" id="nseImportListButton${i}">
  1024. <span class="nseEmoji">⤵️</span>
  1025. Import your NSE tag whitelist into the above field
  1026. </span>
  1027. `;
  1028. } else if(textareas[i].name == "nottags") {
  1029. newElement.innerHTML = `
  1030. <span class="nseNiceButton" id="nseImportListButton${i}">
  1031. <span class="nseEmoji">⤵️</span>
  1032. Import your NSE tag blacklist${nseHardPassEnabled ? " and Hard Pass list" : ""} into the above field
  1033. </span>
  1034. `;
  1035. }
  1036.  
  1037. newElement.onclick = function() {
  1038. let coupledTextarea = this.parentNode.parentNode.querySelector("textarea.long");
  1039.  
  1040. if(coupledTextarea.name == "tags") {
  1041. coupledTextarea.innerHTML = document.getElementById("nseWhitelistTaglistArea").innerHTML;
  1042. } else if(coupledTextarea.name == "nottags") {
  1043. if(nseHardPassEnabled) {
  1044. coupledTextarea.innerHTML = document.getElementById("nseHardPassTaglistArea").innerHTML + " ";
  1045. } else {
  1046. coupledTextarea.innerHTML = "";
  1047. }
  1048.  
  1049. coupledTextarea.innerHTML = coupledTextarea.innerHTML + document.getElementById("nseBlacklistTaglistArea").innerHTML;
  1050. }
  1051. };
  1052.  
  1053. textareas[i].parentNode.querySelector("textarea.long + p").appendChild(newElement);
  1054. }
  1055. }
  1056.  
  1057. // +---------------------+
  1058. // | Main filtering loop |
  1059. // +---------------------+
  1060. let count = 0;
  1061. let torrents = document.querySelectorAll("tr.torrent");
  1062.  
  1063. if(currentPage == "Requests") {
  1064. torrents = document.querySelectorAll("#request_table tr");
  1065. }
  1066.  
  1067. if(torrents) {
  1068. if(!nseUnfilteredPages.includes(currentPage)) { // Skip filtering where it doesn't apply
  1069. for(let i = 0; i < torrents.length; i++) {
  1070. let tagElement = torrents[i].querySelector("td > div.tags");
  1071. if(!tagElement) {
  1072. continue; // skip to next iteration if we can't get taglist
  1073. }
  1074. let russianRouletteBulletInChamber = false;
  1075. if(nseRussianRouletteEnabled) {
  1076. let randNum = Math.floor(Math.random() * 6) + 1; // 1/6 chance to fire
  1077. if(randNum == 6) {
  1078. russianRouletteBulletInChamber = true;
  1079. }
  1080. }
  1081. let uploaderElement, titleElement;
  1082.  
  1083. if(currentPage == "Top 10") {
  1084. uploaderElement = torrents[i].querySelector("td:nth-child(10) > a");
  1085. } else {
  1086. uploaderElement = torrents[i].querySelector("td.user > a");
  1087. }
  1088. if(currentPage == "Collage") {
  1089. titleElement = torrents[i].querySelector("td > strong > a");
  1090. } else {
  1091. titleElement = torrents[i].querySelector("td > a");
  1092. }
  1093. let countMe = 1;
  1094. let currentHidden = false;
  1095. let currentWhitelisted = false;
  1096. let currentBypassWhitelist = false;
  1097. let currentForceHide = false;
  1098. let currentForceShow = false;
  1099. // Check if we are adding the hide button
  1100. if(nseIndividualUploadHidingEnabled) {
  1101. let torrentIconContainer = torrents[i].querySelector("td > span.torrent_icon_container");
  1102. if(torrentIconContainer) {
  1103. let nseToggleHideElement = document.createElement("span");
  1104. nseToggleHideElement.className = "icon";
  1105. nseToggleHideElement.innerHTML = `
  1106. <div class="icon_container">
  1107. <div class="icon_stack">
  1108. <i class="font_icon torrent_icons clickable nseToggleIcon" id="nseToggleIcon_${i}">
  1109. <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAC6npUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHja7ZdtktwoDIb/c4ocAUkIieNgPqpygz1+XrDb0z2T3SS1+2urTdlggSX5fWR6Joy/vs/wDQeVzCGpeS45RxyppMIVA4/nUfaVYtrX84avOXq1h3uCYRL0ct5avdZX2PXjgUcMOl7twa8Z9ssR3Y73ISvyGvfnJGHn007pclTGOcjF7TnV43LUroU7letMd1pnt+7Di8GgUlcEEuYhJHFf/cxAzrMu+75CFYl7zEJhd493hSAvr/foY3wW6EXkxyh8Vv8efRKf62WXT1rmSyMMfjpB+skudxh+Dix3Rvw6YfJw9VXkObvPOc63qylD0XxV1BabHm6w8IDksh/LaIZTMbbdCprHGhuQ99jigdaoEEP9GShRp0qTxu4bNaSYeLChZ24AsmwuxoUbGJGk1WiySZEuDliNRxCBme9caMctO14jR+ROWMoEZ4RH/raFf5r8kxbmbEsiin5rhbx41TXSWOTWFasAhObFTbfAj3bhj0/1s0o1YdmS2fGCNR6ni0Ppo7ZkcxasU/TnJ0TB+uUAEiG2IhkUf6KYSZQyRWM2IujoAFSROUviAwRIlTuS5CSC/cjYecXGM0Z7LStnXmbsTQChksXApkgFrJQU9WPJUUNVRZOqZjX1oEVrlpyy5pwtr02umlgytWxmbsWqiydXz27uXrwWLoI9UEsuVryUUiuHikAVvirWV1gOPuRIhx75sMOPctSG8mmpacvNmrfSaucuHdtEz92699LroDCwU4w0dORhw0cZdaLWpsw0deZp02eZ9aZ2Uf3S/oAaXdR4k1rr7KYGazB7uKC1nehiBmKcCMRtEUBB82IWnVLiRW4xi4XxUSgjSV1sQqdFDAjTINZJN7sPcr/FLaj/Fjf+Fbmw0P0X5ALQfeX2E2p9/c61Tez8CpemUfD1YX54Dex1/ajVf9u/Hb0dvR29Hb0dvR29Hf0PHE388YB/YsMPGOidZZTdxfYAAAGFaUNDUElDQyBwcm9maWxlAAB4nH2RPUjDQBiG37ZKpVYcLCLikKE6WRAVESetQhEqhFqhVQeTS/+gSUOS4uIouBYc/FmsOrg46+rgKgiCPyBOjk6KLlLid0mhRYx3HPfw3ve+3H0H+OtlppodY4CqWUYqERcy2VUh+IoQzX50Y0Zipj4nikl4jq97+Ph+F+NZ3nV/jh4lZzLAJxDPMt2wiDeIpzYtnfM+cYQVJYX4nHjUoAsSP3JddvmNc8FhP8+MGOnUPHGEWCi0sdzGrGioxJPEUUXVKN+fcVnhvMVZLVdZ8578heGctrLMdVpDSGARSxAhQEYVJZRhIUa7RoqJFJ3HPfyDjl8kl0yuEhg5FlCBCsnxg//B796a+YlxNykcBzpfbPtjGAjuAo2abX8f23bjBAg8A1day1+pA9OfpNdaWvQI6N0GLq5bmrwHXO4AA0+6ZEiOFKDlz+eB9zP6pizQdwuE1ty+Nc9x+gCkqVfJG+DgEBgpUPa6x7u72vv2b02zfz9/THKseNROhQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+QKChYVGrZwy10AAAF0SURBVDjL3dK/S9ZhFAXwj74ZvoXwllAKiSZYQ0uIQaND0BKUjQ1ODYIOBiKIEA1RNLWECIUQqIuL4BJFFoElmhjakk4a+YMgQYkIhVqu8PDw/gHRnZ7vufd7OPeew79WheR9ErcxF3g1zqMdF1CJPRyUIzqSvKvQiz94h5f4hvXoN6MeixjGC/wqR1qDFnxCX4IXQ91h3cAbvEZdTnIG27iCElbxAOcwH9/v0ZScpAc/Yn1wHNPoTIiPYgP34qcCurGLU8ncTXyIbXTgeabwetxjFgMJ/gjj2ewIbsECLmfNVdTiBL7iaeLsWjbbiqXKsLs1a37HReygAVdDWQEr2WxbuOxsWHo6aXZgM7lHETO4n5HUh5DGQ+BOHPdYMtSPn3iCwYjGOh5Hv4QtdKXJno1AjuFzqHmLqYhANT7ibhCV8BtfMAQVmdR2PMQ+XkWG1iLtTbiECYxiMl21okzCi3HoaxG22sB3QsGzMGMmbF/2f9dfX9xN/BNad7IAAAAASUVORK5CYII=" />
  1110. </i>
  1111. </div>
  1112. </div>
  1113. `;
  1114. nseToggleHideElement.title = "Filter this torrent with NSE";
  1115. nseToggleHideElement.classList.add("nseToggleHideButton");
  1116. let actualIcon = nseToggleHideElement.querySelector("div > div > i");
  1117. let torrentID = titleElement.href.match(/([0-9]+)/)[0];
  1118. actualIcon.torrentID = torrentID;
  1119. actualIcon.onclick = function() {
  1120. let torrentParent = this.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
  1121.  
  1122. // Refresh lists, they might have been changed in other tabs
  1123. nseIndividualUploadHidingBlacklist = GM_getValue("nseIndividualUploadHidingBlacklist", new Array(0));
  1124. nseIndividualUploadHidingWhitelist = GM_getValue("nseIndividualUploadHidingWhitelist", new Array(0));
  1125. if(torrentParent) {
  1126. if(!torrentParent.getAttribute("isNSEHidden")) {
  1127. torrentParent.setAttribute("isNSEHidden", "0");
  1128. }
  1129. if(torrentParent.getAttribute("isNSEHidden") === "1") {
  1130. // Blacklisted, move to whitelist and unhide
  1131. let currindex = nseIndividualUploadHidingBlacklist.indexOf(this.torrentID);
  1132. if(currindex > -1) {
  1133. nseIndividualUploadHidingBlacklist.splice(currindex, 1);
  1134. }
  1135. currindex = nseIndividualUploadHidingWhitelist.indexOf(this.torrentID);
  1136. if(currindex == -1) { // Only add if not already whitelisted to avoid duplicates
  1137. nseIndividualUploadHidingWhitelist.push(this.torrentID);
  1138. }
  1139. if(currentPage == "Top 10") {
  1140. let count = 0;
  1141. let torrentLinks = document.querySelectorAll("tr.torrent > td > a");
  1142. for(let i = 0; i < torrentLinks.length; i++) {
  1143. let regex = "id=" + this.torrentID;
  1144.  
  1145. if(torrentLinks[i].href.match(regex)) {
  1146. let currParent = torrentLinks[i].parentNode.parentNode;
  1147. currParent.classList.toggle("hidden");
  1148. currParent.setAttribute("isNSEHidden", "0");
  1149. currParent.style.backgroundColor = null;
  1150.  
  1151. let currIcon = currParent.querySelector(".nseToggleIcon");
  1152. currIcon.classList.add("nseIndividuallyWhitelisted");
  1153. currIcon.classList.remove("nseIndividuallyBlacklisted");
  1154. currIcon.classList.remove("nseIndividuallyUntouched");
  1155.  
  1156. count--;
  1157. }
  1158. }
  1159.  
  1160. adjustHiddenHeaderCount(count);
  1161. } else {
  1162. torrentParent.classList.toggle("hidden");
  1163. torrentParent.setAttribute("isNSEHidden", "0");
  1164. torrentParent.style.backgroundColor = null;
  1165. this.classList.add("nseIndividuallyWhitelisted");
  1166. this.classList.remove("nseIndividuallyBlacklisted");
  1167. this.classList.remove("nseIndividuallyUntouched");
  1168.  
  1169. adjustHiddenHeaderCount(-1);
  1170. }
  1171. } else if(torrentParent.getAttribute("isNSEHidden") == "0") {
  1172. // Not hidden or is whitelisted, move to blacklist and hide
  1173. let currindex = nseIndividualUploadHidingWhitelist.indexOf(this.torrentID);
  1174. if(currindex > -1) {
  1175. nseIndividualUploadHidingWhitelist.splice(currindex, 1);
  1176. }
  1177. currindex = nseIndividualUploadHidingBlacklist.indexOf(this.torrentID);
  1178. if(currindex == -1) { // Only add if not already blacklisted to avoid duplicates
  1179. nseIndividualUploadHidingBlacklist.push(this.torrentID);
  1180. }
  1181.  
  1182. if(currentPage == "Top 10") {
  1183. let count = 0;
  1184. let torrentLinks = document.querySelectorAll("tr.torrent > td > a");
  1185. for(let i = 0; i < torrentLinks.length; i++) {
  1186. let regex = "id=" + this.torrentID;
  1187.  
  1188. if(torrentLinks[i].href.match(regex)) {
  1189. let currParent = torrentLinks[i].parentNode.parentNode;
  1190. currParent.classList.toggle("hidden");
  1191. currParent.setAttribute("isNSEHidden", "1");
  1192. currParent.style.backgroundColor = themes[nseSelectedTheme].hiddenBackgroundColor;
  1193.  
  1194. let currIcon = currParent.querySelector(".nseToggleIcon");
  1195. currIcon.classList.remove("nseIndividuallyWhitelisted");
  1196. currIcon.classList.remove("nseIndividuallyUntouched");
  1197. currIcon.classList.add("nseIndividuallyBlacklisted");
  1198.  
  1199. count++;
  1200. }
  1201. }
  1202.  
  1203. adjustHiddenHeaderCount(count);
  1204. } else {
  1205. torrentParent.classList.toggle("hidden");
  1206. torrentParent.setAttribute("isNSEHidden", "1");
  1207. torrentParent.style.backgroundColor = themes[nseSelectedTheme].hiddenBackgroundColor;
  1208.  
  1209. this.classList.remove("nseIndividuallyWhitelisted");
  1210. this.classList.remove("nseIndividuallyUntouched");
  1211. this.classList.add("nseIndividuallyBlacklisted");
  1212.  
  1213. adjustHiddenHeaderCount(1);
  1214. }
  1215. }
  1216. //Save lists immediately after manipulating them
  1217. GM_setValue("nseIndividualUploadHidingBlacklist", nseIndividualUploadHidingBlacklist);
  1218. GM_setValue("nseIndividualUploadHidingWhitelist", nseIndividualUploadHidingWhitelist);
  1219.  
  1220. if(nseRenumberTorrentsEnabled && currentPage == "Top 10") {
  1221. renumberTorrents();
  1222. }
  1223. }
  1224. };
  1225.  
  1226. // Clear list status on right-click
  1227. actualIcon.addEventListener('contextmenu', function(event) {
  1228. event.preventDefault();
  1229.  
  1230. let torrentParent = this.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
  1231.  
  1232. // Refresh lists, they might have been changed in other tabs
  1233. nseIndividualUploadHidingBlacklist = GM_getValue("nseIndividualUploadHidingBlacklist", new Array(0));
  1234. nseIndividualUploadHidingWhitelist = GM_getValue("nseIndividualUploadHidingWhitelist", new Array(0));
  1235. if(torrentParent) {
  1236. // Remove from both lists
  1237. let wasRemovedFromWL = false;
  1238. let wasRemovedFromBL = false;
  1239. let currindex = nseIndividualUploadHidingWhitelist.indexOf(this.torrentID);
  1240. if(currindex > -1) {
  1241. nseIndividualUploadHidingWhitelist.splice(currindex, 1);
  1242. wasRemovedFromWL = true;
  1243. }
  1244.  
  1245. currindex = nseIndividualUploadHidingBlacklist.indexOf(this.torrentID);
  1246. if(currindex > -1) {
  1247. nseIndividualUploadHidingBlacklist.splice(currindex, 1);
  1248. wasRemovedFromBL = true;
  1249. }
  1250. if(wasRemovedFromWL || wasRemovedFromBL) {
  1251. if(currentPage == "Top 10") {
  1252. let count = 0;
  1253. let torrentLinks = document.querySelectorAll("tr.torrent > td > a");
  1254. for(let i = 0; i < torrentLinks.length; i++) {
  1255. let regex = "id=" + this.torrentID;
  1256.  
  1257. if(torrentLinks[i].href.match(regex)) {
  1258. let currParent = torrentLinks[i].parentNode.parentNode;
  1259. currParent.setAttribute("isNSEHidden", "0");
  1260. currParent.style.backgroundColor = null;
  1261.  
  1262. if(!wasRemovedFromWL) {
  1263. currParent.classList.toggle("hidden");
  1264. }
  1265.  
  1266. let currIcon = currParent.querySelector(".nseToggleIcon");
  1267. currIcon.classList.remove("nseIndividuallyWhitelisted");
  1268. currIcon.classList.remove("nseIndividuallyBlacklisted");
  1269. currIcon.classList.add("nseIndividuallyUntouched");
  1270.  
  1271. count--;
  1272. }
  1273. }
  1274. if(wasRemovedFromBL) {
  1275. adjustHiddenHeaderCount(count);
  1276. }
  1277. } else {
  1278. if(!wasRemovedFromWL) {
  1279. torrentParent.classList.toggle("hidden");
  1280. }
  1281.  
  1282. torrentParent.setAttribute("isNSEHidden", "0");
  1283. torrentParent.style.backgroundColor = null;
  1284.  
  1285. this.classList.remove("nseIndividuallyWhitelisted");
  1286. this.classList.remove("nseIndividuallyBlacklisted");
  1287. this.classList.add("nseIndividuallyUntouched");
  1288. if(wasRemovedFromBL) {
  1289. adjustHiddenHeaderCount(-1);
  1290. }
  1291. }
  1292.  
  1293. // Save lists immediately after manipulating them
  1294. GM_setValue("nseIndividualUploadHidingBlacklist", nseIndividualUploadHidingBlacklist);
  1295. GM_setValue("nseIndividualUploadHidingWhitelist", nseIndividualUploadHidingWhitelist);
  1296. }
  1297.  
  1298. if(nseRenumberTorrentsEnabled && currentPage == "Top 10") {
  1299. renumberTorrents();
  1300. }
  1301. }
  1302. }, false);
  1303.  
  1304. torrentIconContainer.appendChild(nseToggleHideElement);
  1305.  
  1306. // Check if torrent is black/whitelisted this way
  1307. let currentBLIndex = nseIndividualUploadHidingBlacklist.indexOf(torrentID);
  1308. if(currentBLIndex > -1) {
  1309. if(russianRouletteBulletInChamber == false) {
  1310. actualIcon.classList.add("nseIndividuallyBlacklisted");
  1311. currentHidden = true;
  1312. currentForceHide = true;
  1313. }
  1314. }
  1315. let currentWLIndex = nseIndividualUploadHidingWhitelist.indexOf(torrentID);
  1316. if(currentWLIndex > -1) {
  1317. actualIcon.classList.add("nseIndividuallyWhitelisted");
  1318. currentWhitelisted = true;
  1319. currentForceShow = true;
  1320. }
  1321. if(currentForceHide == false && currentForceShow == false) {
  1322. // Torrent not acted upon
  1323. actualIcon.classList.add("nseIndividuallyUntouched");
  1324. }
  1325. }
  1326. }
  1327. if(nseObliviousModeEnabled == true) {
  1328. tagElement.classList.add("hidden");
  1329. }
  1330. // Check if filtered via options
  1331. if(nseHideReportedEnabled) {
  1332. if(titleElement.querySelector("span.reported") !== null) {
  1333. currentHidden = true;
  1334. }
  1335. }
  1336. if(nseHideWarnedEnabled) {
  1337. if(torrents[i].classList.contains("redbar")) {
  1338. currentHidden = true;
  1339. }
  1340. }
  1341. if(nseHideUnseededEnabled) {
  1342. let selector = "td:nth-child(8)";
  1343. if(currentPage == "Collage") {
  1344. selector = "td:nth-child(7)";
  1345. }
  1346. let seeders = torrents[i].querySelector(selector);
  1347. if(seeders.classList.contains("r00")) {
  1348. currentHidden = true;
  1349. seeders.innerHTML = "(0)";
  1350. }
  1351. }
  1352. let torrentIconElement = torrents[i].querySelector("td > span.torrent_icon_container > span.icon > a > div.icon_container > div.icon_stack > i");
  1353. if(torrentIconElement) {
  1354. if(nseHideSnatchedEnabled) {
  1355. if(torrentIconElement.classList.contains("snatched")) {
  1356. currentHidden = true;
  1357. if(nseBypassWhitelistsEnabled) {
  1358. currentBypassWhitelist = true;
  1359. }
  1360. }
  1361. }
  1362. if(nseHideSeedingEnabled) {
  1363. if(torrentIconElement.classList.contains("seeding")) {
  1364. currentHidden = true;
  1365. if(nseBypassWhitelistsEnabled) {
  1366. currentBypassWhitelist = true;
  1367. }
  1368. }
  1369. }
  1370. if(nseHideGrabbedEnabled) {
  1371. if(torrentIconElement.classList.contains("grabbed")) {
  1372. currentHidden = true;
  1373. if(nseBypassWhitelistsEnabled) {
  1374. currentBypassWhitelist = true;
  1375. }
  1376. }
  1377. }
  1378. if(nseHideLeechingEnabled) {
  1379. if(torrentIconElement.classList.contains("leeching")) {
  1380. currentHidden = true;
  1381. if(nseBypassWhitelistsEnabled) {
  1382. currentBypassWhitelist = true;
  1383. }
  1384. }
  1385. }
  1386. }
  1387.  
  1388. if(nseHideBookmarkedEnabled) {
  1389. let bmElement = torrents[i].querySelector("i.bookmarked");
  1390. if(bmElement) {
  1391. currentHidden = true;
  1392. if(nseBypassWhitelistsEnabled) {
  1393. currentBypassWhitelist = true;
  1394. }
  1395. }
  1396. }
  1397. // Check uploaders
  1398. // No uploaders on collage and user uploaded pages
  1399. if(currentPage !== "Collage" && currentPage !== "Uploaded") {
  1400. if(!uploaderElement) { // If it is null, it's an anon upload
  1401. if(nseHideAnonUploadsEnabled) {
  1402. let anonName = torrents[i].querySelector("td > span.anon_name");
  1403. if(anonName && anonName.innerHTML == "anon") {
  1404. currentHidden = true;
  1405. if(russianRouletteBulletInChamber == false) {
  1406. anonName.classList.add("nseHiddenUploader");
  1407. }
  1408. }
  1409. }
  1410. } else { // Not an anon upload
  1411. let uploader = uploaderElement.innerHTML.trim().toLowerCase();
  1412. let hiddenByUploader = false;
  1413. for(let l = 0; l < nseBlacklistUploaders.length; l++) {
  1414. if(uploader == nseBlacklistUploaders[l].trim().toLowerCase()) {
  1415. hiddenByUploader = true;
  1416. currentHidden = true;
  1417. if(russianRouletteBulletInChamber == false) {
  1418. uploaderElement.classList.add("nseHiddenUploader");
  1419. }
  1420. break;
  1421. }
  1422. }
  1423. // If hiddenByUploader is still false, that means no blacklisted uploader was found, check whitelist
  1424. // If hiddenByUploader is true, blacklisted uploader was found, and since usernames are unique, skip check
  1425. if(hiddenByUploader == false) {
  1426. if(currentBypassWhitelist == false) {
  1427. for(let m = 0; m < nseWhitelistUploaders.length; m++) {
  1428. if(uploader == nseWhitelistUploaders[m].trim().toLowerCase()) {
  1429. currentWhitelisted = true;
  1430. uploaderElement.classList.add("nseWhitelistedUploader");
  1431. break;
  1432. }
  1433. }
  1434. }
  1435. }
  1436. }
  1437. }
  1438. // Scan title for nseBlacklistTitlePhrases
  1439. // ...For every blacklisted phrase:
  1440. for(let tblCount = 0; tblCount < nseBlacklistTitlePhrases.length; tblCount++) {
  1441. let currentTBLPhrase = nseBlacklistTitlePhrases[tblCount].trim().toLowerCase();
  1442. let torrentTitle = titleElement.innerHTML.trim().toLowerCase();
  1443. if(currentTBLPhrase != "") {
  1444. if(torrentTitle.includes(currentTBLPhrase)) {
  1445. currentHidden = true;
  1446. if(russianRouletteBulletInChamber == false) {
  1447. titleElement.innerHTML = titleElement.innerHTML + ` <color class="nseHiddenTitle">(${currentTBLPhrase})</color>`;
  1448. }
  1449. }
  1450. }
  1451. }
  1452. // Scan title for nseWhitelistTitlePhrases
  1453. // ...For every whitelisted phrase:
  1454. if(currentBypassWhitelist == false) {
  1455. for(let tblCount = 0; tblCount < nseWhitelistTitlePhrases.length; tblCount++) {
  1456. let currentTWLPhrase = nseWhitelistTitlePhrases[tblCount].trim().toLowerCase();
  1457. let torrentTitle = titleElement.innerHTML.trim().toLowerCase();
  1458. if(currentTWLPhrase != "") {
  1459. if(torrentTitle.includes(currentTWLPhrase)) {
  1460. currentWhitelisted = true;
  1461. titleElement.innerHTML = titleElement.innerHTML + ` <color class="nseWhitelistedTitle">(${currentTWLPhrase})</color>`;
  1462. }
  1463. }
  1464. }
  1465. }
  1466. // For every tag in the current torrent
  1467. let tagList = tagElement.querySelectorAll("a");
  1468. if(tagList) {
  1469. for(let k = 0; k < tagList.length; k++) {
  1470. if(nseHardPassEnabled) {
  1471. if(nseHardPassTags.includes(tagList[k].innerHTML) === true) {
  1472. currentHidden = true;
  1473. currentForceHide = true;
  1474. if(russianRouletteBulletInChamber == false) {
  1475. tagList[k].classList.add("nseHardPassTag");
  1476. if(nseRemoveHardPassResults) {
  1477. torrents[i].classList.add("nseHardPassRemove");
  1478. countMe = 0;
  1479. }
  1480. }
  1481. }
  1482. }
  1483. if(nseBlacklistTags.includes(tagList[k].innerHTML) === true) {
  1484. currentHidden = true;
  1485. if(russianRouletteBulletInChamber == false) {
  1486. tagList[k].classList.add("nseHiddenTag");
  1487. }
  1488. }
  1489. if(currentBypassWhitelist == false) {
  1490. if(nseWhitelistTags.includes(tagList[k].innerHTML) === true) {
  1491. currentWhitelisted = true;
  1492. tagList[k].classList.add("nseWhitelistedTag");
  1493. }
  1494. }
  1495. }
  1496. }
  1497. // If forcing through individual filtering, ignore results
  1498. if(currentForceShow) {
  1499. currentHidden = false;
  1500. currentWhitelisted = true;
  1501. } else if(currentForceHide) {
  1502. currentHidden = true;
  1503. currentWhitelisted = false;
  1504. }
  1505. if(currentWhitelisted === true) {
  1506. torrents[i].classList.remove("hidden");
  1507. torrents[i].setAttribute("isNSEHidden", "0");
  1508. } else if(currentHidden === true) {
  1509. if(russianRouletteBulletInChamber == false) {
  1510. torrents[i].style.backgroundColor = themes[nseSelectedTheme].hiddenBackgroundColor;
  1511. torrents[i].classList.add("hidden");
  1512. torrents[i].setAttribute("isNSEHidden", "1");
  1513. count += countMe;
  1514. }
  1515. }
  1516. }
  1517. } else if(currentPage == "Torrent details") {
  1518. if(nseHideUnseededEnabled) {
  1519. let seedersElement = document.querySelector("div.top_info > table.boxstat > tbody > tr > td:nth-child(4)");
  1520. let seedersElement2 = document.querySelector("tr.group_torrent > td:nth-child(5)");
  1521. let currSeeders = seedersElement2.innerHTML;
  1522.  
  1523. if(currSeeders.trim() === "0") {
  1524. console.log("unseeded");
  1525. seedersElement.classList.add("nseBlacklistIdentifier");
  1526. seedersElement2.classList.add("nseBlacklistIdentifier");
  1527.  
  1528. seedersElement.innerHTML = "(" + seedersElement.innerHTML + ")"; // This has a div element also
  1529. seedersElement2.innerHTML = "(0)";
  1530. }
  1531. }
  1532.  
  1533. // Highlight if uploader is anon and user has NSE set to hide anon uploads
  1534. let anonName = document.querySelectorAll(".anon_name");
  1535. if(anonName.length) {
  1536. if(nseHideAnonUploadsEnabled) {
  1537. for(let anon = 0; anon < anonName.length; anon++) {
  1538. anonName[anon].classList.add("nseHiddenUploader");
  1539. }
  1540. }
  1541. // Highlight if uploader is any of the lists
  1542. } else {
  1543. let uploaderElement = document.querySelector("table.boxstat > tbody > tr > td:nth-child(1) > a");
  1544. let uploaderElement2 = document.querySelector(".torrent_table > tbody > tr:nth-child(5) > td > em > a");
  1545.  
  1546. if(uploaderElement) {
  1547. let uploader = uploaderElement.innerHTML.trim().toLowerCase();
  1548. for(let l = 0; l < nseBlacklistUploaders.length; l++) {
  1549. if(uploader == nseBlacklistUploaders[l].trim().toLowerCase()) {
  1550. uploaderElement.classList.add("nseHiddenUploader");
  1551. uploaderElement2.classList.add("nseHiddenUploader");
  1552. break;
  1553. }
  1554. }
  1555. for(let m = 0; m < nseWhitelistUploaders.length; m++) {
  1556. if(uploader == nseWhitelistUploaders[m].trim().toLowerCase()) {
  1557. uploaderElement.classList.add("nseWhitelistedUploader");
  1558. uploaderElement2.classList.add("nseWhitelistedUploader");
  1559. break;
  1560. }
  1561. }
  1562. }
  1563. }
  1564.  
  1565. let titleElement2 = document.querySelector(".torrent_table > tbody > tr:nth-child(2) > td:nth-child(2) > strong");
  1566. let titleElement = document.querySelector("#content > div:nth-child(1) > h2:nth-child(1)");
  1567. let firstSpan = document.querySelector("#content > div:nth-child(1) > h2:nth-child(1) > span:nth-child(1)");
  1568. let h2textChild = Array.from(titleElement.childNodes).filter(node => node.nodeType === 3 && node.textContent.trim().length > 1)[0];
  1569.  
  1570. let newSpan = document.createElement('span');
  1571. h2textChild.after(newSpan);
  1572. newSpan.appendChild(h2textChild);
  1573. titleElement = newSpan;
  1574.  
  1575. // Scan title for nseBlacklistTitlePhrases
  1576. // ...For every blacklisted phrase:
  1577. for(let tblCount = 0; tblCount < nseBlacklistTitlePhrases.length; tblCount++) {
  1578. let currentTBLPhrase = nseBlacklistTitlePhrases[tblCount].trim().toLowerCase();
  1579. let torrentTitle = titleElement.innerHTML.trim().toLowerCase();
  1580.  
  1581. if(currentTBLPhrase != "") {
  1582. if(torrentTitle.includes(currentTBLPhrase)) {
  1583. titleElement.innerHTML = titleElement.innerHTML + ` <color class="nseHiddenTitle">(${currentTBLPhrase})</color>`;
  1584. titleElement2.innerHTML = titleElement2.innerHTML + ` <color class="nseHiddenTitle">(${currentTBLPhrase})</color>`;
  1585. }
  1586. }
  1587. }
  1588.  
  1589. // Scan title for nseWhitelistTitlePhrases
  1590. // ...For every whitelisted phrase:
  1591. for(let tblCount = 0; tblCount < nseWhitelistTitlePhrases.length; tblCount++) {
  1592. let currentTWLPhrase = nseWhitelistTitlePhrases[tblCount].trim().toLowerCase();
  1593. let torrentTitle = titleElement.innerHTML.trim().toLowerCase();
  1594.  
  1595. if(currentTWLPhrase != "") {
  1596. if(torrentTitle.includes(currentTWLPhrase)) {
  1597. titleElement.innerHTML = titleElement.innerHTML + ` <color class="nseWhitelistedTitle">(${currentTWLPhrase})</color>`;
  1598. titleElement2.innerHTML = titleElement2.innerHTML + ` <color class="nseWhitelistedTitle">(${currentTWLPhrase})</color>`;
  1599. }
  1600. }
  1601. }
  1602.  
  1603.  
  1604. // Add individual filter icon to torrent details page, a lot of duplicated code, will function-ize later
  1605. if(nseIndividualUploadHidingEnabled) {
  1606. let torrentIconContainer = document.querySelector("td > span.torrent_icon_container");
  1607.  
  1608. if(torrentIconContainer) {
  1609. let nseToggleHideElement = document.createElement("span");
  1610. nseToggleHideElement.className = "icon";
  1611. nseToggleHideElement.innerHTML = `
  1612. <div class="icon_container">
  1613. <div class="icon_stack">
  1614. <i class="font_icon torrent_icons clickable">
  1615. <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAC6npUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHja7ZdtktwoDIb/c4ocAUkIieNgPqpygz1+XrDb0z2T3SS1+2urTdlggSX5fWR6Joy/vs/wDQeVzCGpeS45RxyppMIVA4/nUfaVYtrX84avOXq1h3uCYRL0ct5avdZX2PXjgUcMOl7twa8Z9ssR3Y73ISvyGvfnJGHn007pclTGOcjF7TnV43LUroU7letMd1pnt+7Di8GgUlcEEuYhJHFf/cxAzrMu+75CFYl7zEJhd493hSAvr/foY3wW6EXkxyh8Vv8efRKf62WXT1rmSyMMfjpB+skudxh+Dix3Rvw6YfJw9VXkObvPOc63qylD0XxV1BabHm6w8IDksh/LaIZTMbbdCprHGhuQ99jigdaoEEP9GShRp0qTxu4bNaSYeLChZ24AsmwuxoUbGJGk1WiySZEuDliNRxCBme9caMctO14jR+ROWMoEZ4RH/raFf5r8kxbmbEsiin5rhbx41TXSWOTWFasAhObFTbfAj3bhj0/1s0o1YdmS2fGCNR6ni0Ppo7ZkcxasU/TnJ0TB+uUAEiG2IhkUf6KYSZQyRWM2IujoAFSROUviAwRIlTuS5CSC/cjYecXGM0Z7LStnXmbsTQChksXApkgFrJQU9WPJUUNVRZOqZjX1oEVrlpyy5pwtr02umlgytWxmbsWqiydXz27uXrwWLoI9UEsuVryUUiuHikAVvirWV1gOPuRIhx75sMOPctSG8mmpacvNmrfSaucuHdtEz92699LroDCwU4w0dORhw0cZdaLWpsw0deZp02eZ9aZ2Uf3S/oAaXdR4k1rr7KYGazB7uKC1nehiBmKcCMRtEUBB82IWnVLiRW4xi4XxUSgjSV1sQqdFDAjTINZJN7sPcr/FLaj/Fjf+Fbmw0P0X5ALQfeX2E2p9/c61Tez8CpemUfD1YX54Dex1/ajVf9u/Hb0dvR29Hb0dvR29Hf0PHE388YB/YsMPGOidZZTdxfYAAAGFaUNDUElDQyBwcm9maWxlAAB4nH2RPUjDQBiG37ZKpVYcLCLikKE6WRAVESetQhEqhFqhVQeTS/+gSUOS4uIouBYc/FmsOrg46+rgKgiCPyBOjk6KLlLid0mhRYx3HPfw3ve+3H0H+OtlppodY4CqWUYqERcy2VUh+IoQzX50Y0Zipj4nikl4jq97+Ph+F+NZ3nV/jh4lZzLAJxDPMt2wiDeIpzYtnfM+cYQVJYX4nHjUoAsSP3JddvmNc8FhP8+MGOnUPHGEWCi0sdzGrGioxJPEUUXVKN+fcVnhvMVZLVdZ8578heGctrLMdVpDSGARSxAhQEYVJZRhIUa7RoqJFJ3HPfyDjl8kl0yuEhg5FlCBCsnxg//B796a+YlxNykcBzpfbPtjGAjuAo2abX8f23bjBAg8A1day1+pA9OfpNdaWvQI6N0GLq5bmrwHXO4AA0+6ZEiOFKDlz+eB9zP6pizQdwuE1ty+Nc9x+gCkqVfJG+DgEBgpUPa6x7u72vv2b02zfz9/THKseNROhQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+QKChYVGrZwy10AAAF0SURBVDjL3dK/S9ZhFAXwj74ZvoXwllAKiSZYQ0uIQaND0BKUjQ1ODYIOBiKIEA1RNLWECIUQqIuL4BJFFoElmhjakk4a+YMgQYkIhVqu8PDw/gHRnZ7vufd7OPeew79WheR9ErcxF3g1zqMdF1CJPRyUIzqSvKvQiz94h5f4hvXoN6MeixjGC/wqR1qDFnxCX4IXQ91h3cAbvEZdTnIG27iCElbxAOcwH9/v0ZScpAc/Yn1wHNPoTIiPYgP34qcCurGLU8ncTXyIbXTgeabwetxjFgMJ/gjj2ewIbsECLmfNVdTiBL7iaeLsWjbbiqXKsLs1a37HReygAVdDWQEr2WxbuOxsWHo6aXZgM7lHETO4n5HUh5DGQ+BOHPdYMtSPn3iCwYjGOh5Hv4QtdKXJno1AjuFzqHmLqYhANT7ibhCV8BtfMAQVmdR2PMQ+XkWG1iLtTbiECYxiMl21okzCi3HoaxG22sB3QsGzMGMmbF/2f9dfX9xN/BNad7IAAAAASUVORK5CYII=" />
  1616. </i>
  1617. </div>
  1618. </div>
  1619. `;
  1620. nseToggleHideElement.title = "Filter this torrent with NSE";
  1621. nseToggleHideElement.classList.add("nseToggleHideButton");
  1622. let actualIcon = nseToggleHideElement.querySelector("div > div > i");
  1623. let torrentID = window.location.href.match(/id=([0-9]+)/)[1];
  1624. actualIcon.torrentID = torrentID;
  1625. actualIcon.onclick = function() {
  1626. // Refresh lists, they might have been changed in other tabs
  1627. nseIndividualUploadHidingBlacklist = GM_getValue("nseIndividualUploadHidingBlacklist", new Array(0));
  1628. nseIndividualUploadHidingWhitelist = GM_getValue("nseIndividualUploadHidingWhitelist", new Array(0));
  1629.  
  1630. if(nseIndividualUploadHidingBlacklist.includes(torrentID)) {
  1631. // Blacklisted, move to whitelist and unhide
  1632. let currindex = nseIndividualUploadHidingBlacklist.indexOf(this.torrentID);
  1633. if(currindex > -1) {
  1634. nseIndividualUploadHidingBlacklist.splice(currindex, 1);
  1635. }
  1636. currindex = nseIndividualUploadHidingWhitelist.indexOf(this.torrentID);
  1637. if(currindex == -1) { // Only add if not already whitelisted to avoid duplicates
  1638. nseIndividualUploadHidingWhitelist.push(this.torrentID);
  1639. }
  1640. this.classList.add("nseIndividuallyWhitelisted");
  1641. this.classList.remove("nseIndividuallyBlacklisted");
  1642. this.classList.remove("nseIndividuallyUntouched");
  1643. } else {
  1644. // Not hidden or is whitelisted, move to blacklist and hide
  1645. let currindex = nseIndividualUploadHidingWhitelist.indexOf(this.torrentID);
  1646. if(currindex > -1) {
  1647. nseIndividualUploadHidingWhitelist.splice(currindex, 1);
  1648. }
  1649. currindex = nseIndividualUploadHidingBlacklist.indexOf(this.torrentID);
  1650. if(currindex == -1) { // Only add if not already blacklisted to avoid duplicates
  1651. nseIndividualUploadHidingBlacklist.push(this.torrentID);
  1652. }
  1653. this.classList.remove("nseIndividuallyWhitelisted");
  1654. this.classList.remove("nseIndividuallyUntouched");
  1655. this.classList.add("nseIndividuallyBlacklisted");
  1656. }
  1657. //Save lists immediately after manipulating them
  1658. GM_setValue("nseIndividualUploadHidingBlacklist", nseIndividualUploadHidingBlacklist);
  1659. GM_setValue("nseIndividualUploadHidingWhitelist", nseIndividualUploadHidingWhitelist);
  1660. };
  1661.  
  1662. // Clear list status on right-click
  1663. actualIcon.addEventListener('contextmenu', function(event) {
  1664. event.preventDefault();
  1665.  
  1666. // Refresh lists, they might have been changed in other tabs
  1667. nseIndividualUploadHidingBlacklist = GM_getValue("nseIndividualUploadHidingBlacklist", new Array(0));
  1668. nseIndividualUploadHidingWhitelist = GM_getValue("nseIndividualUploadHidingWhitelist", new Array(0));
  1669.  
  1670. // Remove from both lists
  1671. let currindex = nseIndividualUploadHidingWhitelist.indexOf(this.torrentID);
  1672. if(currindex > -1) {
  1673. nseIndividualUploadHidingWhitelist.splice(currindex, 1);
  1674. }
  1675.  
  1676. currindex = nseIndividualUploadHidingBlacklist.indexOf(this.torrentID);
  1677. if(currindex > -1) {
  1678. nseIndividualUploadHidingBlacklist.splice(currindex, 1);
  1679. }
  1680.  
  1681. this.classList.remove("nseIndividuallyWhitelisted");
  1682. this.classList.remove("nseIndividuallyBlacklisted");
  1683. this.classList.add("nseIndividuallyUntouched");
  1684.  
  1685. //Save lists immediately after manipulating them
  1686. GM_setValue("nseIndividualUploadHidingBlacklist", nseIndividualUploadHidingBlacklist);
  1687. GM_setValue("nseIndividualUploadHidingWhitelist", nseIndividualUploadHidingWhitelist);
  1688. }, false);
  1689.  
  1690. torrentIconContainer.appendChild(nseToggleHideElement);
  1691.  
  1692. // Check if torrent is black/whitelisted this way
  1693. let currentBLIndex = nseIndividualUploadHidingBlacklist.indexOf(torrentID);
  1694. if(currentBLIndex > -1) {
  1695. actualIcon.classList.add("nseIndividuallyBlacklisted");
  1696. }
  1697. let currentWLIndex = nseIndividualUploadHidingWhitelist.indexOf(torrentID);
  1698. if(currentWLIndex > -1) {
  1699. actualIcon.classList.add("nseIndividuallyWhitelisted");
  1700. }
  1701.  
  1702. if(currentBLIndex == -1 && currentWLIndex == -1) {
  1703. actualIcon.classList.add("nseIndividuallyUntouched");
  1704. }
  1705. }
  1706. }
  1707. } else if(currentPage == "My uploaded") {
  1708. // Make sure colors function as expected on user uploads pages
  1709. recolorTags();
  1710. }
  1711. }
  1712.  
  1713.  
  1714. // +-----------------------------------------+
  1715. // | Event handler assignment and miscellany |
  1716. // +-----------------------------------------+
  1717.  
  1718. // Renumber if enabled
  1719. if(nseRenumberTorrentsEnabled && currentPage == "Top 10") {
  1720. renumberTorrents();
  1721. }
  1722.  
  1723. // Add classes for Right-Click Management if enabled
  1724. if(nseRightClickManagementEnabled && currentPage !== "Notification filters") {
  1725. if(currentPage == "Torrent details") {
  1726. if(nseRCMTagsEnabled) {
  1727. let currTaglist = document.querySelector("#torrent_tags");
  1728. let observerOptions = {subtree: true, childList: true};
  1729. let mObserver = new MutationObserver(function() {
  1730. let tagList = document.querySelectorAll("ul#torrent_tags_list > li > a");
  1731. if(tagList !== null && tagList !== undefined) {
  1732. for(let i = 0; i < tagList.length; i++) {
  1733. tagList[i].classList.add("nseTagElement");
  1734. }
  1735. }
  1736. });
  1737.  
  1738. mObserver.observe(currTaglist, observerOptions);
  1739. }
  1740.  
  1741. if(nseRCMTitlesEnabled) {
  1742. // "Torrent Info" box
  1743. let titleElement = document.querySelector(".group_torrent > td:nth-child(2) > strong");
  1744. titleElement.classList.add("nseTitleElement");
  1745.  
  1746. // Header text
  1747. titleElement = document.querySelector("#content > div:nth-child(1) > h2:nth-child(1)");
  1748. titleElement.classList.add("nseTitleElement");
  1749. }
  1750.  
  1751. if(nseRCMUploadersEnabled) {
  1752. // First uploader element in torrent stats/controls near top of page
  1753. let uploaderElement = document.querySelector("table.boxstat > tbody > tr > td:nth-child(1) > a");
  1754. if(uploaderElement) {
  1755. uploaderElement.classList.add("nseUploaderElement");
  1756. }
  1757.  
  1758. // Second uploader element in "Torrent Info" box
  1759. uploaderElement = document.querySelector(".torrent_table > tbody > tr:nth-child(5) > td > em > a");
  1760. if(uploaderElement) {
  1761. uploaderElement.classList.add("nseUploaderElement");
  1762. }
  1763. }
  1764. } else {
  1765. for(let i = 0; i < torrents.length; i++) {
  1766. // Tags
  1767. if(nseRCMTagsEnabled) {
  1768. let tagList = torrents[i].querySelectorAll("td > div.tags > a");
  1769. if(tagList !== null && tagList !== undefined) {
  1770. for(let j = 0; j < tagList.length; j++) {
  1771. tagList[j].classList.add("nseTagElement");
  1772. }
  1773. }
  1774. }
  1775. // Titles
  1776. if(nseRCMTitlesEnabled) {
  1777. let titleElement;
  1778. if(currentPage == "Collage") {
  1779. titleElement = torrents[i].querySelector("td > strong > a");
  1780. } else {
  1781. titleElement = torrents[i].querySelector("td > a");
  1782. }
  1783. titleElement.classList.add("nseTitleElement");
  1784. }
  1785.  
  1786. // Uploaders
  1787. if(nseRCMUploadersEnabled) {
  1788. let uploaderElement = torrents[i].querySelector("td.user > a");
  1789. if(currentPage == "Top 10") {
  1790. uploaderElement = torrents[i].querySelector("td:nth-child(10) > a");
  1791. }
  1792. if(currentPage !== "Collage" && currentPage !== "Uploaded" && uploaderElement !== null) {
  1793. uploaderElement.classList.add("nseUploaderElement");
  1794. }
  1795. }
  1796. }
  1797. }
  1798. }
  1799.  
  1800. // Keep torrent details taglist colored when it changes
  1801. if(currentPage == "Torrent details") {
  1802. let currTaglist = document.querySelector("#torrent_tags");
  1803. let observerOptions = {subtree: true, childList: true};
  1804. let mObserver = new MutationObserver(function() { recolorTags(); });
  1805. mObserver.observe(currTaglist, observerOptions);
  1806. }
  1807.  
  1808. function recolorTags() {
  1809. let tagList;
  1810. if(currentPage == "Torrent details") {
  1811. tagList = document.querySelectorAll("ul#torrent_tags_list > li > a");
  1812. } else if(currentPage == "My uploaded") {
  1813. tagList = document.querySelectorAll("div.tags > a");
  1814. } else if(nseUnfilteredPages.includes(currentPage)) {
  1815. return;
  1816. } else {
  1817. tagList = document.querySelectorAll("a.nseTagElement");
  1818. }
  1819. if(tagList !== null && tagList !== undefined) {
  1820. for(let i = 0; i < tagList.length; i++) {
  1821. if(nseBlacklistTags.includes(tagList[i].innerHTML.trim())) {
  1822. tagList[i].classList.remove("nseHardPassTag");
  1823. tagList[i].classList.remove("nseWhitelistedTag");
  1824. tagList[i].classList.add("nseHiddenTag");
  1825. } else if(nseHardPassEnabled && nseHardPassTags.includes(tagList[i].innerHTML.trim())) {
  1826. tagList[i].classList.remove("nseHiddenTag");
  1827. tagList[i].classList.remove("nseWhitelistedTag");
  1828. tagList[i].classList.add("nseHardPassTag");
  1829. } else if(nseWhitelistTags.includes(tagList[i].innerHTML.trim())) {
  1830. tagList[i].classList.remove("nseHiddenTag");
  1831. tagList[i].classList.remove("nseHardPassTag");
  1832. tagList[i].classList.add("nseWhitelistedTag");
  1833. } else {
  1834. tagList[i].classList.remove("nseHiddenTag");
  1835. tagList[i].classList.remove("nseHardPassTag");
  1836. tagList[i].classList.remove("nseWhitelistedTag");
  1837. }
  1838. }
  1839. }
  1840. }
  1841.  
  1842. function recolorUploaders() {
  1843. let uploaderElements;
  1844.  
  1845. if(currentPage == "Top 10") {
  1846. uploaderElements = document.querySelectorAll("td:nth-child(10) > a");
  1847. } else if(nseUnfilteredPages.includes(currentPage) || currentPage == "Collage"|| currentPage == "Uploaded") {
  1848. return;
  1849. } else {
  1850. uploaderElements = document.querySelectorAll("td.user > a");
  1851. }
  1852.  
  1853. if(uploaderElements) {
  1854. for(let i = 0; i < uploaderElements.length; i++) {
  1855. if(nseBlacklistUploaders.includes(uploaderElements[i].innerHTML.trim())) {
  1856. uploaderElements[i].classList.remove("nseWhitelistedUploader");
  1857. uploaderElements[i].classList.add("nseHiddenUploader");
  1858. } else if(nseWhitelistUploaders.includes(uploaderElements[i].innerHTML.trim())) {
  1859. uploaderElements[i].classList.remove("nseHiddenUploader");
  1860. uploaderElements[i].classList.add("nseWhitelistedUploader");
  1861. } else {
  1862. uploaderElements[i].classList.remove("nseHiddenUploader");
  1863. uploaderElements[i].classList.remove("nseWhitelistedUploader");
  1864. }
  1865. }
  1866. }
  1867. }
  1868.  
  1869. // Remove categories if enabled
  1870. if(nseHideCategoryIconsEnabled && currentPage !== "Notification filters" && currentPage !== "Torrent details") {
  1871. let selector = ".cats_col";
  1872. if(currentPage == "Collage" || currentPage == "Uploaded") {
  1873. selector = "#torrent_table > tbody > tr > td:nth-child(1)";
  1874. } else if(currentPage == "Notifications") {
  1875. selector = ".cats_cols";
  1876. }
  1877.  
  1878. let catsCols = document.querySelectorAll(selector);
  1879. for(let i = 0; i < catsCols.length; i++) {
  1880. catsCols[i].nextElementSibling.setAttribute("colspan", "2");
  1881. catsCols[i].style.display = "none";
  1882. }
  1883. }
  1884.  
  1885. // Remove Hard Pass results if Black Hole is enabled
  1886. if(nseHardPassEnabled && nseRemoveHardPassResults && !nseUnfilteredPages.includes(currentPage)) {
  1887. let elementsToRemove = document.querySelectorAll(".nseHardPassRemove");
  1888. if(elementsToRemove) {
  1889. for(let i = 0; i < elementsToRemove.length; i++) {
  1890. if(elementsToRemove[i]) {
  1891. elementsToRemove[i].remove();
  1892. }
  1893. }
  1894. }
  1895. }
  1896.  
  1897. // Assign event handlers if arrow key navigation is enabled
  1898. if(nseArrowNavigationEnabled && currentPage !== "Notification filters" && currentPage !== "Torrent details") {
  1899. document.onkeydown = function(event) {
  1900. if(event.target.nodeName !== "TEXTAREA" && event.target.nodeName !== "INPUT") {
  1901. if (event.code == "ArrowLeft") {
  1902. nseGoBack(event);
  1903. } else if (event.code == "ArrowRight") {
  1904. nseGoForward(event);
  1905. }
  1906. }
  1907. };
  1908. }
  1909.  
  1910. let headerNode = document.getElementById("nseHeaderText");
  1911.  
  1912. if(nseUnfilteredPages.includes(currentPage)) {
  1913. headerNode.innerHTML = "Filtering is disabled on this page";
  1914. headerNode.style.cursor = "auto";
  1915. } else {
  1916. adjustHiddenHeaderCount(count);
  1917.  
  1918. headerNode.onclick = function() {
  1919. if(headerNode.innerHTML.match(/([0-9]+)/) !== null) {
  1920. toggleTorrents();
  1921. }
  1922. };
  1923. }
  1924.  
  1925. document.getElementById("nseToggleOptionsNode").onclick = function() {
  1926. document.getElementById("nseMainDiv").classList.toggle("hidden");
  1927. };
  1928.  
  1929. document.getElementById("nseCloseOptionsButton").onclick = function() {
  1930. document.getElementById("nseMainDiv").classList.add("hidden");
  1931. document.getElementById("nseOuter").scrollIntoView();
  1932. };
  1933.  
  1934. document.getElementById("nseCheckCustomCSS").onclick = function() {
  1935. if(this.checked) {
  1936. document.getElementById("nseCustomCSSDiv").classList.remove("hidden");
  1937. } else {
  1938. document.getElementById("nseCustomCSSDiv").classList.add("hidden");
  1939. }
  1940. };
  1941.  
  1942. document.getElementById("nseCheckRightClickManagementEnabled").onclick = function() {
  1943. if(this.checked) {
  1944. document.getElementById("nseRCMCustomizationDiv").classList.remove("hidden");
  1945. } else {
  1946. document.getElementById("nseRCMCustomizationDiv").classList.add("hidden");
  1947. }
  1948. };
  1949.  
  1950. document.getElementById("nseThemeDropdown").onchange = function() {
  1951. let selectedTheme = this.options[this.selectedIndex].value;
  1952. let descriptionNode = document.getElementById("nseThemeDescription");
  1953.  
  1954. if(selectedTheme == "nseThemeCustom") {
  1955. document.getElementById("nseCustomThemeDiv").classList.remove("hidden");
  1956. } else {
  1957. document.getElementById("nseCustomThemeDiv").classList.add("hidden");
  1958. }
  1959.  
  1960. descriptionNode.innerHTML = nseThemeDescriptions[selectedTheme];
  1961. };
  1962.  
  1963. if(nseOpenAllButtonEnabled && !nseUnfilteredPages.includes(currentPage)) {
  1964. document.getElementById("nseOpenAllButton").onclick = function() {
  1965. let torrentLinks;
  1966.  
  1967. if(currentPage == "Collage") {
  1968. torrentLinks = document.querySelectorAll("tr.torrent > td > strong > a.nseTitleElement");
  1969. } else {
  1970. torrentLinks = document.querySelectorAll("tr.torrent > td > a.nseTitleElement");
  1971. }
  1972. if(torrentLinks) {
  1973. let visitedLinks = [];
  1974. for(let i = 0; i < torrentLinks.length; i++) {
  1975. let hiddenStatus;
  1976.  
  1977. if(currentPage == "Collage") {
  1978. hiddenStatus = torrentLinks[i].parentNode.parentNode.parentNode.getAttribute("isNSEHidden");
  1979. } else {
  1980. hiddenStatus = torrentLinks[i].parentNode.parentNode.getAttribute("isNSEHidden");
  1981. }
  1982.  
  1983. if(hiddenStatus == 0 || hiddenStatus == "" || hiddenStatus == null) {
  1984. if(!visitedLinks.includes(torrentLinks[i].href)) {
  1985. window.open(torrentLinks[i].href, "_blank");
  1986. visitedLinks.push(torrentLinks[i].href);
  1987. }
  1988. }
  1989. }
  1990.  
  1991. if(nseOpenAllGoNextEnabled) {
  1992. nseGoForward();
  1993. }
  1994. }
  1995. };
  1996. }
  1997.  
  1998. if(nseFilterAllButtonEnabled && nseIndividualUploadHidingEnabled && !nseUnfilteredPages.includes(currentPage)) {
  1999. document.getElementById("nseFilterAllButton").onclick = function() {
  2000. if(confirm(`Are you sure you want to filter all unfiltered results on this page?\n\nThis cannot be automatically undone, and you will have to manually unhide each torrent to undo it.`)) {
  2001. for(let i = 0; i < torrents.length; i++) {
  2002. if(!torrents[i].getAttribute("isNSEHidden") || torrents[i].getAttribute("isNSEHidden") === "0") {
  2003. torrents[i].querySelector(".nseToggleIcon").click();
  2004. }
  2005. }
  2006. }
  2007. };
  2008. }
  2009.  
  2010. // We like to have fun around here 😏
  2011. {
  2012. let now = new Date();
  2013. if(nseEveryDayIsApril1st || (now.getMonth() == 3 && now.getDate() == 1)) {
  2014. let emojiElements = document.getElementsByClassName("nseEmoji");
  2015. let normieArray = new Array("😂","🤣","😆", "🍆", "🍑", "💦", "💯", "😭", "🙃", "💩", "👌", "😏", "🙄", "🥵", "😱", "👀", "🤡");
  2016. for(let i = 0; i < emojiElements.length; i++) {
  2017. let randNum = Math.floor(Math.random() * normieArray.length);
  2018. emojiElements[i].innerHTML = normieArray[randNum];
  2019. }
  2020. }
  2021. }
  2022.  
  2023. let nseTextAreas = new Array("nseBlacklistTaglistArea", "nseHardPassTaglistArea", "nseWhitelistTaglistArea","nseBlacklistTitleListArea","nseWhitelistTitleListArea","nseBlacklistUploadersArea","nseWhitelistUploadersArea");
  2024.  
  2025. for(let textAreaCounter = 0; textAreaCounter < nseTextAreas.length; textAreaCounter++) {
  2026. document.getElementById(nseTextAreas[textAreaCounter]).addEventListener("keydown", function(event) {
  2027. if ((window.navigator.platform.match("Mac") ? event.metaKey : event.ctrlKey) && event.code == "KeyS") {
  2028. event.preventDefault();
  2029. saveData();
  2030. }
  2031. }, false);
  2032. }
  2033.  
  2034. let explanationTogglers = new Array("nseBLE", "nseHPE", "nseWLE","nseTitleBLE", "nseTitleWLE", "nseUBLE", "nseUWLE");
  2035.  
  2036. for(let i = 0; i < explanationTogglers.length; i++) {
  2037. document.getElementById(explanationTogglers[i] + "Toggler").onclick = function() {
  2038. document.getElementById(this.id.substring(0, this.id.length - 7)).classList.toggle("hidden");
  2039. };
  2040. }
  2041.  
  2042. if(nseRightClickManagementEnabled && currentPage !== "Notification filters") {
  2043. if(currentPage == "Torrent details") {
  2044. let currTaglist = document.querySelector("#torrent_tags");
  2045. let observerOptions = {subtree: true, childList: true};
  2046. let mObserver = new MutationObserver(function() {
  2047. let allTagElements = document.querySelectorAll(".nseTagElement");
  2048. for(let i = 0; i < allTagElements.length; i++) {
  2049. allTagElements[i].addEventListener('contextmenu', function(event) {
  2050. event.preventDefault();
  2051. showRCMBox("tag", this.innerHTML.trim(), event.pageX, event.pageY);
  2052. }, false);
  2053. }
  2054. });
  2055.  
  2056. mObserver.observe(currTaglist, observerOptions);
  2057. } else {
  2058. let allTagElements = document.querySelectorAll(".nseTagElement");
  2059. for(let i = 0; i < allTagElements.length; i++) {
  2060. allTagElements[i].addEventListener('contextmenu', function(event) {
  2061. event.preventDefault();
  2062. showRCMBox("tag", this.innerHTML.trim(), event.pageX, event.pageY);
  2063. }, false);
  2064. }
  2065. }
  2066.  
  2067. let allUploaderElements = document.querySelectorAll(".nseUploaderElement");
  2068. for(let j = 0; j < allUploaderElements.length; j++) {
  2069. allUploaderElements[j].addEventListener('contextmenu', function(event) {
  2070. event.preventDefault();
  2071. showRCMBox("uploader", this.innerHTML.trim(), event.pageX, event.pageY);
  2072. }, false);
  2073. }
  2074.  
  2075. let allTitleElements = document.querySelectorAll(".nseTitleElement");
  2076. for(let k = 0; k < allTitleElements.length; k++) {
  2077. allTitleElements[k].addEventListener('contextmenu', function(event) {
  2078. event.preventDefault();
  2079.  
  2080. // Strip already hidden title fragments, if any
  2081. let currTitle = this.innerHTML;
  2082. let colorIndex = currTitle.indexOf("<color");
  2083. if(colorIndex != -1) {
  2084. currTitle = currTitle.substring(0, colorIndex);
  2085. }
  2086.  
  2087. showRCMBox("title", currTitle.trim(), event.pageX, event.pageY);
  2088. }, false);
  2089. }
  2090.  
  2091. document.getElementById("nseRCMClose").onclick = function() {
  2092. this.parentNode.classList.add("hidden");
  2093. };
  2094. }
  2095.  
  2096. document.getElementById("nseExportButton").onclick = function() { exportSettings(); };
  2097. document.getElementById("nseImportFilePicker").onchange = function () { importSettings(event); };
  2098. document.getElementById("nseEraseDataButton").onclick = function () { resetSettings(); };
  2099. document.getElementById("nseSaveButton").onclick = function() { saveData(); };
  2100. document.getElementById("nseReloadButton").onclick = function() { location.reload(); };
  2101. // End event handler assignment section
  2102.  
  2103.  
  2104. // +-----------+
  2105. // | Functions |
  2106. // +-----------+
  2107. function toggleTorrents() {
  2108. for(let k = 0; k < torrents.length; k++) {
  2109. torrents[k].classList.toggle("hidden");
  2110. }
  2111. }
  2112.  
  2113. function renumberTorrents() {
  2114. let torrentTables = document.querySelectorAll("table.torrent_table");
  2115. for(let i = 0; i < torrentTables.length; i++) {
  2116. let currTorrents = torrentTables[i].querySelectorAll("tr.torrent");
  2117. if(currTorrents) {
  2118. let shownNum = 1;
  2119. let hiddenNum = 1;
  2120. for(let j = 0; j < currTorrents.length; j++) {
  2121. if(currTorrents[j].classList.contains("hidden")) {
  2122. currTorrents[j].querySelector("td > strong").innerHTML = hiddenNum;
  2123. hiddenNum++;
  2124. } else {
  2125. currTorrents[j].querySelector("td > strong").innerHTML = shownNum;
  2126. shownNum++;
  2127. }
  2128. }
  2129. }
  2130. }
  2131. }
  2132.  
  2133. function adjustHiddenHeaderCount(value) {
  2134. let currNum = headerNode.innerHTML.match(/([0-9]+)/);
  2135. if(currNum === null) {
  2136. currNum = value;
  2137. } else {
  2138. currNum = currNum[0];
  2139. currNum = Number(currNum) + value;
  2140. }
  2141.  
  2142. if(currNum === 0) {
  2143. headerNode.innerHTML = "NoShitEmpornium";
  2144. if(value !== 0) {
  2145. toggleTorrents();
  2146. }
  2147. } else if(currNum === 1) {
  2148. headerNode.innerHTML = "Toggle 1 hidden torrent";
  2149. } else {
  2150. headerNode.innerHTML = "Toggle " + currNum + " hidden torrents";
  2151. }
  2152. }
  2153.  
  2154. function showRCMBox(boxType, elementValue, mouseX, mouseY) {
  2155. let box = document.getElementById("nseRCMBox");
  2156. let x = mouseX;
  2157. let y = mouseY;
  2158.  
  2159. // Make sure the box stays within page bounds
  2160. if(x - 150 < 0) {
  2161. x = 0;
  2162. } else if(x + 320 > window.innerWidth) {
  2163. x = window.innerWidth - 350;
  2164. } else {
  2165. x -= 150;
  2166. }
  2167.  
  2168. box.style.left = x + "px";
  2169. box.style.top = y - 90 + "px";
  2170.  
  2171. let nseRCMBoxInfoText = box.querySelector("#nseRCMBoxInfoText");
  2172. if(boxType == "tag") {
  2173. let infoText = `<b>Tag:</b> <span class="nseMonospace" id="nseRCMBoxTag">${elementValue}</span><br /><br />`;
  2174.  
  2175. let currTagBlacklist = document.getElementById("nseBlacklistTaglistArea").value.split(" ");
  2176. let currTagHardPassList = document.getElementById("nseHardPassTaglistArea").value.split(" ");
  2177. let currTagWhitelist = document.getElementById("nseWhitelistTaglistArea").value.split(" ");
  2178. let nseRCMBoxChoices = document.getElementById("nseRCMBoxChoices");
  2179.  
  2180. if(currTagBlacklist.includes(elementValue)) { // Current tag is in blacklist
  2181. infoText = infoText + `This tag was found in your <span class="nseBlacklistIdentifier">blacklist</span>!`;
  2182.  
  2183. nseRCMBoxChoices.innerHTML = `
  2184. <span class="nseRCMButton" id="nseRCMBoxBLRemove">${nseEmojiEnabled ? '➖' : '-'} Remove from blacklist</span><br /><br />
  2185. ${nseHardPassEnabled ?
  2186. `<span class="nseRCMButton" id="nseRCMBoxHPAdd">${nseEmojiEnabled ? '➕' : '+'} Move to Hard Pass</span><br /><br />`
  2187. : ''}
  2188. <span class="nseRCMButton" id="nseRCMBoxWLAdd">${nseEmojiEnabled ? '➕' : '+'} Move to whitelist</span><br />
  2189. `;
  2190.  
  2191. document.getElementById("nseRCMBoxBLRemove").onclick = function() {
  2192. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2193. removeItemFromList("nseBlacklistTaglistArea", currTag);
  2194. saveData();
  2195. recolorTags();
  2196. closeRCMBox();
  2197. };
  2198.  
  2199. if(nseHardPassEnabled) {
  2200. document.getElementById("nseRCMBoxHPAdd").onclick = function() {
  2201. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2202. addItemToList("nseHardPassTaglistArea", "nseBlacklistTaglistArea", currTag);
  2203. saveData();
  2204. recolorTags();
  2205. closeRCMBox();
  2206. };
  2207. }
  2208.  
  2209. document.getElementById("nseRCMBoxWLAdd").onclick = function() {
  2210. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2211. addItemToList("nseWhitelistTaglistArea", "nseBlacklistTaglistArea", currTag);
  2212. saveData();
  2213. recolorTags();
  2214. closeRCMBox();
  2215. };
  2216. } else if(currTagWhitelist.includes(elementValue)) { // Current tag is in whitelist
  2217. infoText = infoText + `This tag was found in your <span class="nseWhitelistIdentifier">whitelist</span>!`;
  2218.  
  2219. nseRCMBoxChoices.innerHTML = `
  2220. <span class="nseRCMButton" id="nseRCMBoxWLRemove">${nseEmojiEnabled ? '➖' : '-'} Remove from whitelist</span><br /><br />
  2221. <span class="nseRCMButton" id="nseRCMBoxBLAdd">${nseEmojiEnabled ? '➕' : '+'} Move to blacklist</span><br /><br />
  2222. ${nseHardPassEnabled ?
  2223. `<span class="nseRCMButton" id="nseRCMBoxHPAdd">${nseEmojiEnabled ? '➕' : '+'} Move to Hard Pass</span><br />`
  2224. : ''}
  2225. `;
  2226.  
  2227. document.getElementById("nseRCMBoxWLRemove").onclick = function() {
  2228. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2229. removeItemFromList("nseWhitelistTaglistArea", currTag);
  2230. saveData();
  2231. recolorTags();
  2232. closeRCMBox();
  2233. };
  2234.  
  2235. document.getElementById("nseRCMBoxBLAdd").onclick = function() {
  2236. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2237. addItemToList("nseBlacklistTaglistArea", "nseWhitelistTaglistArea", currTag);
  2238. saveData();
  2239. recolorTags();
  2240. closeRCMBox();
  2241. };
  2242.  
  2243. if(nseHardPassEnabled) {
  2244. document.getElementById("nseRCMBoxHPAdd").onclick = function() {
  2245. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2246. addItemToList("nseHardPassTaglistArea", "nseWhitelistTaglistArea", currTag);
  2247. saveData();
  2248. recolorTags();
  2249. closeRCMBox();
  2250. };
  2251. }
  2252. } else if(nseHardPassEnabled && currTagHardPassList.includes(elementValue)) {
  2253. // Current tag is in Hard Pass and it is enabled
  2254. infoText = infoText + `This tag was found in your <span class="nseBlacklistIdentifier">Hard Pass blacklist</span>!`;
  2255. nseRCMBoxChoices.innerHTML = `
  2256. <span class="nseRCMButton" id="nseRCMBoxHPRemove">${nseEmojiEnabled ? '➖' : '-'} Remove from Hard Pass</span><br /><br />
  2257. <span class="nseRCMButton" id="nseRCMBoxBLAdd">${nseEmojiEnabled ? '➕' : '+'} Move to blacklist</span><br /><br />
  2258. <span class="nseRCMButton" id="nseRCMBoxWLAdd">${nseEmojiEnabled ? '➕' : '+'} Move to whitelist</span><br />
  2259. `;
  2260. document.getElementById("nseRCMBoxHPRemove").onclick = function() {
  2261. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2262. removeItemFromList("nseHardPassTaglistArea", currTag);
  2263. saveData();
  2264. recolorTags();
  2265. closeRCMBox();
  2266. };
  2267. document.getElementById("nseRCMBoxBLAdd").onclick = function() {
  2268. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2269. addItemToList("nseBlacklistTaglistArea", "nseHardPassTaglistArea", currTag);
  2270. saveData();
  2271. recolorTags();
  2272. closeRCMBox();
  2273. };
  2274. document.getElementById("nseRCMBoxWLAdd").onclick = function() {
  2275. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2276. addItemToList("nseWhitelistTaglistArea", "nseHardPassTaglistArea", currTag);
  2277. saveData();
  2278. recolorTags();
  2279. closeRCMBox();
  2280. };
  2281. } else { // Current tag is in no list
  2282. infoText = infoText + "This tag was not found in any of your taglists.";
  2283.  
  2284. nseRCMBoxChoices.innerHTML = `
  2285. <span class="nseRCMButton" id="nseRCMBoxBLAdd">${nseEmojiEnabled ? '➕' : '+'} Add to blacklist</span><br /><br />
  2286. ${nseHardPassEnabled ?
  2287. `<span class="nseRCMButton" id="nseRCMBoxHPAdd">${nseEmojiEnabled ? '➕' : '+'} Add to Hard Pass</span><br /><br />`
  2288. : ''}
  2289. <span class="nseRCMButton" id="nseRCMBoxWLAdd">${nseEmojiEnabled ? '➕' : '+'} Add to whitelist</span><br />
  2290. `;
  2291.  
  2292. document.getElementById("nseRCMBoxBLAdd").onclick = function() {
  2293. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2294. addItemToList("nseBlacklistTaglistArea", "nseWhitelistTaglistArea", currTag);
  2295. saveData();
  2296. recolorTags();
  2297. closeRCMBox();
  2298. };
  2299.  
  2300. if(nseHardPassEnabled) {
  2301. document.getElementById("nseRCMBoxHPAdd").onclick = function() {
  2302. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2303. addItemToList("nseHardPassTaglistArea", "nseWhitelistTaglistArea", currTag);
  2304. saveData();
  2305. recolorTags();
  2306. closeRCMBox();
  2307. };
  2308. }
  2309.  
  2310. document.getElementById("nseRCMBoxWLAdd").onclick = function() {
  2311. let currTag = document.getElementById("nseRCMBoxTag").innerHTML;
  2312. addItemToList("nseWhitelistTaglistArea", "nseBlacklistTaglistArea", currTag);
  2313. saveData();
  2314. recolorTags();
  2315. closeRCMBox();
  2316. };
  2317. }
  2318.  
  2319. nseRCMBoxInfoText.innerHTML = infoText;
  2320. } else if(boxType == "uploader") {
  2321. let infoText = "<b>Uploader:</b> <span class=\"nseMonospace\" id=\"nseRCMBoxUploader\">" + elementValue + "</span><br /><br />";
  2322.  
  2323. let currUploaderBlacklist = document.getElementById("nseBlacklistUploadersArea").value.split(" ");
  2324. let currUploaderWhitelist = document.getElementById("nseWhitelistUploadersArea").value.split(" ");
  2325. let nseRCMBoxChoices = document.getElementById("nseRCMBoxChoices");
  2326.  
  2327. if(currUploaderBlacklist.includes(elementValue)) { // Current uploader is in blacklist
  2328. infoText = infoText + `This uploader was found in your <span class="nseBlacklistIdentifier">blacklist</span>!`;
  2329.  
  2330. nseRCMBoxChoices.innerHTML = `
  2331. <span class="nseRCMButton" id="nseRCMBoxBLRemove">${nseEmojiEnabled ? '➖' : '-'} Remove from blacklist</span><br /><br />
  2332. <span class="nseRCMButton" id="nseRCMBoxWLAdd">${nseEmojiEnabled ? '➕' : '+'} Move to whitelist</span><br />
  2333. `;
  2334.  
  2335. document.getElementById("nseRCMBoxBLRemove").onclick = function() {
  2336. let currTag = document.getElementById("nseRCMBoxUploader").innerHTML;
  2337. removeItemFromList("nseBlacklistUploadersArea", currTag);
  2338. saveData();
  2339. recolorUploaders();
  2340. closeRCMBox();
  2341. };
  2342.  
  2343. document.getElementById("nseRCMBoxWLAdd").onclick = function() {
  2344. let currTag = document.getElementById("nseRCMBoxUploader").innerHTML;
  2345. addItemToList("nseWhitelistUploadersArea", "nseBlacklistUploadersArea", currTag);
  2346. saveData();
  2347. recolorUploaders();
  2348. closeRCMBox();
  2349. };
  2350. } else if(currUploaderWhitelist.includes(elementValue)) { // Current uploader is in whitelist
  2351. infoText = infoText + `This uploader was found in your <span class="nseWhitelistIdentifier">whitelist</span>!`;
  2352.  
  2353. nseRCMBoxChoices.innerHTML = `
  2354. <span class="nseRCMButton" id="nseRCMBoxWLRemove">${nseEmojiEnabled ? '➖' : '-'} Remove from whitelist</span><br /><br />
  2355. <span class="nseRCMButton" id="nseRCMBoxBLAdd">${nseEmojiEnabled ? '➕' : '+'} Move to blacklist</span><br />
  2356. `;
  2357.  
  2358. document.getElementById("nseRCMBoxWLRemove").onclick = function() {
  2359. let currTag = document.getElementById("nseRCMBoxUploader").innerHTML;
  2360. removeItemFromList("nseWhitelistUploadersArea", currTag);
  2361. saveData();
  2362. recolorUploaders();
  2363. closeRCMBox();
  2364. };
  2365.  
  2366. document.getElementById("nseRCMBoxBLAdd").onclick = function() {
  2367. let currTag = document.getElementById("nseRCMBoxUploader").innerHTML;
  2368. addItemToList("nseBlacklistUploadersArea", "nseWhitelistUploadersArea", currTag);
  2369. saveData();
  2370. recolorUploaders();
  2371. closeRCMBox();
  2372. };
  2373. } else { // Current uploader is in no list
  2374. infoText = infoText + "This uploader was not found in any of your uploader lists.";
  2375.  
  2376. nseRCMBoxChoices.innerHTML = `
  2377. <span class="nseRCMButton" id="nseRCMBoxBLAdd">${nseEmojiEnabled ? '➕' : '+'} Add to blacklist</span><br /><br />
  2378. <span class="nseRCMButton" id="nseRCMBoxWLAdd">${nseEmojiEnabled ? '➕' : '+'} Add to whitelist</span><br />
  2379. `;
  2380.  
  2381. document.getElementById("nseRCMBoxBLAdd").onclick = function() {
  2382. let currTag = document.getElementById("nseRCMBoxUploader").innerHTML;
  2383. addItemToList("nseBlacklistUploadersArea", "nseWhitelistUploadersArea", currTag);
  2384. saveData();
  2385. recolorUploaders();
  2386. closeRCMBox();
  2387. };
  2388.  
  2389. document.getElementById("nseRCMBoxWLAdd").onclick = function() {
  2390. let currTag = document.getElementById("nseRCMBoxUploader").innerHTML;
  2391. addItemToList("nseWhitelistUploadersArea", "nseBlacklistUploadersArea", currTag);
  2392. saveData();
  2393. recolorUploaders();
  2394. closeRCMBox();
  2395. };
  2396. }
  2397.  
  2398. nseRCMBoxInfoText.innerHTML = infoText;
  2399. } else if(boxType == "title") {
  2400. let infoText = `Customize the title phrase you would like to filter in the text box below. You can use a semicolon <span class="nseMonospace">;</span> to separate multiple phrases:<br /><br />
  2401. <input type="text" class="nseInput" id="nseRCMTitlePhraseText" value="${elementValue.replace(/"/g, '&quot;')}"></input><br />`;
  2402. nseRCMBoxInfoText.innerHTML = infoText;
  2403.  
  2404. let nseRCMBoxChoices = document.getElementById("nseRCMBoxChoices");
  2405.  
  2406. nseRCMBoxChoices.innerHTML = `
  2407. <span class="nseRCMButton" id="nseRCMBoxBLAdd">${nseEmojiEnabled ? '➕' : '+'} Add phrase(s) to title blacklist</span><br /><br />
  2408. <span class="nseRCMButton" id="nseRCMBoxWLAdd">${nseEmojiEnabled ? '➕' : '+'} Add phrase(s) to title whitelist</span><br />
  2409. `;
  2410.  
  2411. document.getElementById("nseRCMBoxWLAdd").onclick = function() {
  2412. let currTitlePhrase = document.getElementById("nseRCMTitlePhraseText").value;
  2413. addItemToList("nseWhitelistTitleListArea", "nseBlacklistTitleListArea", currTitlePhrase);
  2414. saveData();
  2415. closeRCMBox();
  2416. };
  2417.  
  2418. document.getElementById("nseRCMBoxBLAdd").onclick = function() {
  2419. let currTitlePhrase = document.getElementById("nseRCMTitlePhraseText").value;
  2420.  
  2421. addItemToList("nseBlacklistTitleListArea", "nseWhitelistTitleListArea", currTitlePhrase);
  2422. saveData();
  2423. closeRCMBox();
  2424. };
  2425. }
  2426. box.classList.remove("hidden");
  2427. return false;
  2428. }
  2429.  
  2430. function closeRCMBox() {
  2431. document.getElementById("nseRCMBox").classList.add("hidden");
  2432. }
  2433.  
  2434. function removeItemFromList(tagListAreaName, tagName) {
  2435. let currList = document.getElementById(tagListAreaName).value.trim().split(" ");
  2436.  
  2437. let index = currList.indexOf(tagName);
  2438. if (index > -1) {
  2439. currList.splice(index, 1);
  2440. }
  2441.  
  2442. document.getElementById(tagListAreaName).value = currList.join(" ");
  2443. // Show the refresh button because we've done changes
  2444. document.getElementById("nseDynamicRefreshNode").classList.remove("hidden");
  2445. }
  2446.  
  2447. function addItemToList(tagListAreaName, oppositeTagListAreaName, filterValue) {
  2448. let splitChar = " ";
  2449. if(tagListAreaName == "nseWhitelistTitleListArea" || tagListAreaName == "nseBlacklistTitleListArea") {
  2450. splitChar = ";";
  2451. } else {
  2452. // Only do opposite removal for tags/uploaders
  2453. removeItemFromList(oppositeTagListAreaName, filterValue);
  2454. }
  2455.  
  2456. let currList = document.getElementById(tagListAreaName).value.trim().split(splitChar);
  2457.  
  2458. if(splitChar == ";") {
  2459. // Since titles can include more than one phrase, iterate through an exploded array
  2460. let titleArray = filterValue.split(splitChar);
  2461.  
  2462. for(let l = 0; l < titleArray.length; l++) {
  2463. let currPhrase = titleArray[l].trim();
  2464. if(currPhrase != "") {
  2465. currList.push(currPhrase);
  2466. }
  2467. }
  2468. } else {
  2469. currList.push(filterValue);
  2470. }
  2471. document.getElementById(tagListAreaName).value = currList.join(splitChar).replace(/;/,';');
  2472.  
  2473. // Show the refresh button because we did changes
  2474. document.getElementById("nseDynamicRefreshNode").classList.remove("hidden");
  2475. }
  2476.  
  2477. function downloadFile(filename, text) {
  2478. let element = document.createElement('a');
  2479. element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text));
  2480. element.setAttribute("download", filename);
  2481. element.style.display = "none";
  2482. document.body.appendChild(element);
  2483. element.click();
  2484. document.body.removeChild(element);
  2485. }
  2486.  
  2487. function exportSettings(getSize = false) {
  2488. if(!getSize) { saveData(); }
  2489.  
  2490. let settingsNames = GM_listValues();
  2491. let settings = {};
  2492.  
  2493. for(let i = 0; i < settingsNames.length; i++) {
  2494. settings[settingsNames[i]] = GM_getValue(settingsNames[i], null);
  2495. }
  2496.  
  2497. let jsonOutput = JSON.stringify(settings);
  2498.  
  2499. if(getSize) {
  2500. return String(jsonOutput.length);
  2501. } else {
  2502. downloadFile(`nseExport-${Date.now()}.json`, jsonOutput);
  2503. }
  2504. }
  2505.  
  2506. function importSettings(event) {
  2507. let reader = new FileReader();
  2508. reader.onload = function(){
  2509. if(confirm("Are you sure you want to import this file? Your existing settings will be overwritten!")) {
  2510. let settingsObject = JSON.parse(reader.result);
  2511.  
  2512. for(let i = 0; i < Object.keys(settingsObject).length; i++) {
  2513. GM_setValue(Object.keys(settingsObject)[i], settingsObject[Object.keys(settingsObject)[i]]);
  2514. }
  2515.  
  2516. location.reload();
  2517. }
  2518. };
  2519. reader.readAsText(event.target.files[0]);
  2520. }
  2521.  
  2522. function resetSettings() {
  2523. if(confirm("Are you sure you want to reset all blacklists, all whitelists, and all preferences in NSE? This cannot be undone!")) {
  2524. let settingsNames = GM_listValues();
  2525.  
  2526. for(let i = 0; i < settingsNames.length; i++) {
  2527. GM_deleteValue(settingsNames[i]);
  2528. }
  2529.  
  2530. location.reload();
  2531. }
  2532. }
  2533.  
  2534. function saveData() {
  2535. let listsToSave = {
  2536. nseTaglist: "nseBlacklistTaglistArea",
  2537. nseHardPassTaglist: "nseHardPassTaglistArea",
  2538. nseWhitelist: "nseWhitelistTaglistArea",
  2539. nseBlacklistTitles: "nseBlacklistTitleListArea",
  2540. nseWhitelistTitles: "nseWhitelistTitleListArea",
  2541. nseUploaders: "nseBlacklistUploadersArea",
  2542. nseWhitelistUploaders: "nseWhitelistUploadersArea"
  2543. };
  2544.  
  2545. // Convert to lower case, trim whitespace, remove duplicates and save filter lists
  2546. for(const setting in listsToSave) {
  2547. let strList = document.getElementById(listsToSave[setting]).value.trim().toLowerCase();
  2548. let delimiter = " ";
  2549. if(listsToSave[setting].includes("TitleList")) {
  2550. delimiter = ";";
  2551. }
  2552.  
  2553. // Empty diff lists
  2554. let removedList = new Array(0);
  2555. let addedList = new Array(0);
  2556.  
  2557. // List from text boxes
  2558. let freshList = strList.split(delimiter);
  2559.  
  2560. // Stored list (might have changed since we made our cache at the start of the script)
  2561. let storedList = GM_getValue(setting).split(delimiter);
  2562.  
  2563. // Cached copy of stored list
  2564. let cachedList;
  2565. switch(setting) {
  2566. case "nseTaglist":
  2567. cachedList = nseBlacklistTags;
  2568. break;
  2569. case "nseHardPassTaglist":
  2570. cachedList = nseHardPassTags;
  2571. break;
  2572. case "nseWhitelist":
  2573. cachedList = nseWhitelistTags;
  2574. break;
  2575. case "nseBlacklistTitles":
  2576. cachedList = nseBlacklistTitlePhrases;
  2577. break;
  2578. case "nseWhitelistTitles":
  2579. cachedList = nseWhitelistTitlePhrases;
  2580. break;
  2581. case "nseUploaders":
  2582. cachedList = nseBlacklistUploaders;
  2583. break;
  2584. case "nseWhitelistUploaders":
  2585. cachedList = nseWhitelistUploaders;
  2586. break;
  2587. }
  2588.  
  2589. // Compute additions and removals from OG array
  2590. freshList.forEach(function(item){
  2591. if(cachedList.indexOf(item) == -1) {
  2592. addedList.push(item);
  2593. }
  2594. });
  2595.  
  2596. cachedList.forEach(function(item) {
  2597. if(freshList.indexOf(item) == -1) {
  2598. removedList.push(item);
  2599. }
  2600. });
  2601.  
  2602. // Remove removals and add additions to the stored list
  2603. removedList.forEach(function(item) {
  2604. let foundIndex = storedList.indexOf(item);
  2605. if(foundIndex > -1) {
  2606. storedList.splice(foundIndex, 1);
  2607. }
  2608. });
  2609. addedList.forEach(function(item) {
  2610. let foundIndex = storedList.indexOf(item);
  2611. if(foundIndex == -1) {
  2612. if(item !== "" && item !== null) {
  2613. storedList.push(item);
  2614. }
  2615. }
  2616. });
  2617.  
  2618. // Turning it into a set and back into an array will automagically kill dupes
  2619. storedList = [...new Set(storedList)];
  2620. storedList.sort();
  2621.  
  2622. strList = storedList.join(delimiter);
  2623. GM_setValue(setting, strList);
  2624.  
  2625. // Reflect updated list in textarea
  2626. document.getElementById(listsToSave[setting]).value = strList;
  2627.  
  2628. // Update cached copy of list
  2629. switch(setting) {
  2630. case "nseTaglist":
  2631. nseBlacklistTaglist = strList;
  2632. nseBlacklistTags = storedList;
  2633. break;
  2634. case "nseHardPassTaglist":
  2635. nseHardPassTaglist = strList;
  2636. nseHardPassTags = storedList;
  2637. break;
  2638. case "nseWhitelist":
  2639. nseWhitelistTaglist = strList;
  2640. nseWhitelistTags = storedList;
  2641. break;
  2642. case "nseBlacklistTitles":
  2643. nseBlacklistTitleList = strList;
  2644. nseBlacklistTitlePhrases = storedList;
  2645. break;
  2646. case "nseWhitelistTitles":
  2647. nseWhitelistTitleList = strList;
  2648. nseWhitelistTitlePhrases = storedList;
  2649. break;
  2650. case "nseUploaders":
  2651. nseBlacklistUploadersList = strList;
  2652. nseBlacklistUploaders = storedList;
  2653. break;
  2654. case "nseWhitelistUploaders":
  2655. nseWhitelistUploadersList = strList;
  2656. nseWhitelistUploaders = storedList;
  2657. break;
  2658. }
  2659. }
  2660.  
  2661. let checkboxes = {
  2662. nseObliviousModeEnabled: "nseCheckObliviousMode",
  2663. nseRussianRouletteEnabled: "nseCheckRussianRouletteMode",
  2664. nseHideAnonUploadsEnabled: "nseCheckHideAnonUploads",
  2665. nseHideWarnedEnabled: "nseCheckHideWarned",
  2666. nseHideReportedEnabled: "nseCheckHideReported",
  2667. nseHideSnatchedEnabled: "nseCheckHideSnatched",
  2668. nseHideSeedingEnabled: "nseCheckHideSeeding",
  2669. nseHideLeechingEnabled: "nseCheckHideLeeching",
  2670. nseHideGrabbedEnabled: "nseCheckHideGrabbed",
  2671. nseHideBookmarkedEnabled: "nseCheckHideBookmarked",
  2672. nseBypassWhitelistsEnabled: "nseCheckBypassWhitelists",
  2673. nseIndividualUploadHidingEnabled: "nseCheckIndividualHide",
  2674. nseFilterAllButtonEnabled: "nseCheckFilterAllButtonEnabled",
  2675. nseRightClickManagementEnabled: "nseCheckRightClickManagementEnabled",
  2676. nseRCMTagsEnabled: "nseCheckRCMTagsEnabled",
  2677. nseRCMTitlesEnabled: "nseCheckRCMTitlesEnabled",
  2678. nseRCMUploadersEnabled: "nseCheckRCMUploadersEnabled",
  2679. nseEmojiEnabled: "nseCheckEmojiEnabled",
  2680. nseScrollToNSEEnabled: "nseCheckScrollToNSEEnabled",
  2681. nseHardPassEnabled: "nseCheckHardPassEnabled",
  2682. nseRemoveHardPassResults: "nseCheckRemoveHardPassResults",
  2683. nseHideUnseededEnabled: "nseCheckHideUnseeded",
  2684. nseRenumberTorrentsEnabled: "nseCheckRenumberTorrents",
  2685. nseHideCategoryIconsEnabled: "nseCheckHideCategoryIcons",
  2686. nseArrowNavigationEnabled: "nseCheckArrowNavigation",
  2687. nseUpdateToastsEnabled: "nseCheckUpdateToasts",
  2688. nseFloatingToggleButtonEnabled: "nseCheckFloatingToggleButton",
  2689. nseOpenAllButtonEnabled: "nseCheckOpenAllButton",
  2690. nseOpenAllGoNextEnabled: "nseCheckOpenAllGoNext",
  2691. nseCustomCSSEnabled: "nseCheckCustomCSS"
  2692. };
  2693.  
  2694. for(const setting in checkboxes) {
  2695. GM_setValue(setting, document.getElementById(checkboxes[setting]).checked);
  2696. }
  2697.  
  2698. if(nseEnableApril1stOption) {
  2699. GM_setValue("nseEveryDayIsApril1st", document.getElementById("nseCheckApril1stAllYear").checked);
  2700. }
  2701.  
  2702. let nseThemeDropdown = document.getElementById("nseThemeDropdown");
  2703. GM_setValue("nseSelectedTheme", nseThemeDropdown.options[nseThemeDropdown.selectedIndex].value);
  2704.  
  2705. nseCustomTheme = {
  2706. backgroundColor: document.getElementById("nseCustomThemeBgCol").value,
  2707. backgroundHighlightColor: document.getElementById("nseCustomThemeBgHighCol").value,
  2708. foregroundColor: document.getElementById("nseCustomThemeForeCol").value,
  2709. accentColor: document.getElementById("nseCustomThemeAccentCol").value,
  2710. highlightColor: document.getElementById("nseCustomThemeHighCol").value,
  2711. hiddenBackgroundColor: document.getElementById("nseCustomThemeHiddenBgCol").value
  2712. };
  2713. GM_setValue("nseCustomTheme", nseCustomTheme);
  2714.  
  2715. GM_setValue("nseUIFont", document.getElementById("nseUIFont").value);
  2716. GM_setValue("nseTextAreaFont", document.getElementById("nseTextAreaFont").value);
  2717.  
  2718. GM_setValue("nseBlacklistColor", document.getElementById("nseBlacklistColor").value);
  2719. GM_setValue("nseHardPassColor", document.getElementById("nseHardPassColor").value);
  2720. GM_setValue("nseWhitelistColor", document.getElementById("nseWhitelistColor").value);
  2721.  
  2722. GM_setValue("nseTimeout", Number(document.getElementById("nseTimeout").value));
  2723.  
  2724. // We need to escape backslashes in the custom CSS as it will be included in a back-ticked CSS block
  2725. let css = document.getElementById("nseCustomCSSArea").value;
  2726. css = css.replace(/\\/gi, "\\\\");
  2727. GM_setValue("nseCustomCSS", css);
  2728.  
  2729. let time = new Date().toLocaleTimeString();
  2730. document.getElementById("nseSaveDiv").innerHTML = "Saved at " + time + "!";
  2731. document.getElementById("nseSaveDiv").classList.remove("hidden");
  2732. }
  2733.  
  2734. function nseGoBack(event = null) {
  2735. let prevLink = document.getElementsByClassName("pager_prev")[0];
  2736. if(prevLink) {
  2737. if(event) { event.preventDefault(); }
  2738. prevLink.click();
  2739. } else {
  2740. let firstLink = document.getElementsByClassName("pager_first")[0];
  2741. if(firstLink) {
  2742. if(event) { event.preventDefault(); }
  2743. firstLink.click();
  2744. }
  2745. }
  2746. }
  2747.  
  2748. function nseGoForward(event = null) {
  2749. let nextLink = document.getElementsByClassName("pager_next")[0];
  2750. if(nextLink) {
  2751. if(event) { event.preventDefault(); }
  2752. nextLink.click();
  2753. } else {
  2754. let lastLink = document.getElementsByClassName("pager_last")[0];
  2755. if(lastLink) {
  2756. if(event) { event.preventDefault(); }
  2757. lastLink.click();
  2758. }
  2759. }
  2760. }
  2761.  
  2762. // +-----+
  2763. // | CSS |
  2764. // +-----+
  2765. GM_addStyle(`
  2766.  
  2767. .nseOuterDiv, #nseRCMBox {
  2768. font-family: ${nseUIFont} !important;
  2769. margin:auto;
  2770. color: ${themes[nseSelectedTheme].foregroundColor};
  2771. padding: 10px;
  2772. border: 1px solid ${themes[nseSelectedTheme].accentColor};
  2773. box-shadow: 0 1px 3px rgba(0,0,0,.1);
  2774. background-color: ${themes[nseSelectedTheme].backgroundColor} !important;
  2775. border-radius: 20px;
  2776. }
  2777.  
  2778. #nseOuter {
  2779. width: 600px;
  2780. margin-top: 10px;
  2781. }
  2782.  
  2783. p:not(:last-child) {
  2784. margin: 0 0 20px;
  2785. }
  2786.  
  2787. section {
  2788. display: none;
  2789. padding: 20px 0 0;
  2790. border-top: 1px solid ${themes[nseSelectedTheme].accentColor};
  2791. }
  2792.  
  2793. #nseUpdateToast {
  2794. font-family: ${nseUIFont} !important;
  2795. z-index: 9001 !important;
  2796. position: fixed;
  2797. bottom: 0px;
  2798. width: 100%;
  2799. height: 80px;
  2800. text-align: center;
  2801. color: ${themes[nseSelectedTheme].foregroundColor};
  2802. background-color: ${themes[nseSelectedTheme].backgroundColor};
  2803. border-top: 3px solid ${themes[nseSelectedTheme].accentColor};
  2804. }
  2805.  
  2806. .nseRadioButton {
  2807. display: none;
  2808. }
  2809.  
  2810. .nseLabel {
  2811. display: inline-block;
  2812. margin: 0 0 -1px;
  2813. padding: 15px;
  2814. font-weight: 600;
  2815. text-align: center;
  2816. color: ${themes[nseSelectedTheme].accentColor};
  2817. background-color: ${themes[nseSelectedTheme].backgroundColor} !important;
  2818. border: 1px solid ${themes[nseSelectedTheme].accentColor};
  2819. margin-right: 5px;
  2820. }
  2821.  
  2822. .nseSettingsCheckbox {
  2823. padding: 5px;
  2824. }
  2825.  
  2826. a.nseLink, a.nseLink:visited {
  2827. color: ${themes[nseSelectedTheme].accentColor};
  2828. }
  2829.  
  2830. .nseSpanLink {
  2831. color: ${themes[nseSelectedTheme].accentColor};
  2832. cursor: pointer;
  2833. }
  2834.  
  2835. .nseLabel:hover {
  2836. color: ${themes[nseSelectedTheme].highlightColor};
  2837. background-color: ${themes[nseSelectedTheme].backgroundHighlightColor} !important;
  2838. }
  2839.  
  2840. .nseRadioButton:checked + .nseLabel {
  2841. color: ${themes[nseSelectedTheme].accentColor};
  2842. border: 1px solid ${themes[nseSelectedTheme].accentColor};
  2843. border-top: 2px solid ${themes[nseSelectedTheme].accentColor};
  2844. border-bottom: 1px solid ${themes[nseSelectedTheme].backgroundColor};
  2845. }
  2846.  
  2847. #nseTab1:checked ~ #nseContent1,#nseTab2:checked ~ #nseContent2,#nseTab3:checked ~ #nseContent3,#nseTab4:checked ~ #nseContent4 {
  2848. display: block;
  2849. }
  2850.  
  2851. #nseSettingsTab1:checked ~ #nseSettingsContent1,#nseSettingsTab2:checked ~ #nseSettingsContent2,#nseSettingsTab3:checked ~ #nseSettingsContent3,#nseSettingsTab4:checked ~ #nseSettingsContent4 {
  2852. display: block;
  2853. }
  2854.  
  2855. .nseExplanationSpan {
  2856. font-size: 14px !important;
  2857. color: ${themes[nseSelectedTheme].accentColor};
  2858. }
  2859.  
  2860. .nseESOffset {
  2861. margin-left: 60px;
  2862. }
  2863.  
  2864. .nseListHeader {
  2865. font-size: 18px;
  2866. }
  2867.  
  2868. .nseImageButton {
  2869. position: relative;
  2870. color: ${themes[nseSelectedTheme].foregroundColor};
  2871. font-weight: bold;
  2872. }
  2873.  
  2874. .nseImageButton > a, .nseImageButton > a:visited {
  2875. color: ${themes[nseSelectedTheme].foregroundColor};
  2876. text-decoration: none;
  2877. border: 0;
  2878. }
  2879.  
  2880. .nseImageButton:before {
  2881. font-weight: normal;
  2882. margin-right: 5px;
  2883. }
  2884.  
  2885. .nseExplanationBox, #nseCustomThemeDiv, #nseCustomCSSDiv, .nseNiceBox {
  2886. width: 97%;
  2887. margin-top: 10px;
  2888. border: 1px solid ${themes[nseSelectedTheme].accentColor};
  2889. padding: 10px;
  2890. border-radius: 10px;
  2891. }
  2892.  
  2893. #nseRCMCustomizationDiv {
  2894. margin-top: 0px;
  2895. }
  2896.  
  2897. .nseNiceBox {
  2898. width: 75%;
  2899. }
  2900.  
  2901. #nseHeader, .nseCenterDiv {
  2902. width: 100%;
  2903. margin: auto;
  2904. text-align: center;
  2905. font-size: 18px;
  2906. }
  2907.  
  2908. .nseExplanationToggler, ${currentPage !== "My uploaded" ? "#nseHeaderText," : ""} .nseNiceButton, .nseRCMButton, .nseLabel:hover {
  2909. cursor: pointer;
  2910. }
  2911.  
  2912. #nseRCMBox {
  2913. width: 300px;
  2914. position: absolute;
  2915. top: 0;
  2916. left: 0;
  2917. z-index: 9001;
  2918. }
  2919.  
  2920. #nseRCMClose {
  2921. float: right;
  2922. cursor: pointer;
  2923. }
  2924.  
  2925. .nseMonospace {
  2926. font-family: Courier New;
  2927. font-size: 16px;
  2928. }
  2929.  
  2930. .nseTextArea {
  2931. font-family: ${nseTextAreaFont} !important;
  2932. width: 99%;
  2933. max-width: 99%;
  2934. }
  2935.  
  2936. .nseTextArea, .nseInput {
  2937. color: ${themes[nseSelectedTheme].foregroundColor};
  2938. background: none !important;
  2939. background-color: ${themes[nseSelectedTheme].backgroundColor};
  2940. border: 1px solid ${themes[nseSelectedTheme].accentColor};
  2941. }
  2942.  
  2943. .nseFieldDiv, .nseExplanationNode, .nseExplanationBox, #nseCustomThemeDiv, #nseCustomCSSDiv, .nseOuterDiv {
  2944. margin-bottom: 10px;
  2945. }
  2946.  
  2947. .nseHiddenUploader, .nseHiddenTag, .nseHiddenTitle, .nseBlacklistIdentifier {
  2948. color: ${nseBlacklistColor} !important;
  2949. font-weight: bold !important;
  2950. }
  2951.  
  2952. .nseWhitelistedUploader, .nseWhitelistedTag, .nseWhitelistedTitle, .nseWhitelistIdentifier {
  2953. color: ${nseWhitelistColor} !important;
  2954. font-weight: bold !important;
  2955. }
  2956.  
  2957. .nseHiddenTag, .nseWhitelistedTag, .nseWhitelistedTitle, .nseHiddenTitle {
  2958. display: inline;
  2959. }
  2960.  
  2961. .nseHardPassTag {
  2962. color: ${nseHardPassColor} !important;
  2963. font-weight: bold !important;
  2964. }
  2965.  
  2966. .nseNiceButton, .nseRCMButton {
  2967. background-color: ${themes[nseSelectedTheme].backgroundColor} !important;
  2968. color: ${themes[nseSelectedTheme].foregroundColor} !important;
  2969. border: 1px solid ${themes[nseSelectedTheme].accentColor};
  2970. border-radius: 10px;
  2971. padding: 5px;
  2972. margin-left: 5px;
  2973. margin-right: 5px;
  2974. font-size: 16px;
  2975. }
  2976.  
  2977. .nseNiceButton:hover, .nseRCMButton:hover {
  2978. background-color: ${themes[nseSelectedTheme].backgroundHighlightColor} !important;
  2979. }
  2980.  
  2981. .nseNiceButton, .nseRCMButton {
  2982. padding-top: 3px;
  2983. }
  2984.  
  2985. .nseBtmAiryDiv {
  2986. margin-bottom: 20px;
  2987. }
  2988.  
  2989. #nseSaveDiv, .nseTopAiryDiv {
  2990. margin-top: 20px;
  2991. }
  2992.  
  2993. #nseSaveDiv, #nseDataUsage {
  2994. font-weight: bold;
  2995. }
  2996.  
  2997. #nseMainDiv, .nseUpdateToastDiv {
  2998. margin-top: 10px;
  2999. }
  3000.  
  3001. h3.nseH3 {
  3002. color: ${themes[nseSelectedTheme].foregroundColor} !important;
  3003. }
  3004.  
  3005. .nseIndividuallyWhitelisted {
  3006. border: 2px solid green;
  3007. background-color: #CEF6D8;
  3008. }
  3009.  
  3010. .nseIndividuallyBlacklisted {
  3011. border: 2px solid red;
  3012. background-color: #F6CED8;
  3013. }
  3014.  
  3015. .nseIndividuallyUntouched {
  3016. border: 2px solid gray;
  3017. background-color: #EBEBEB;
  3018. }
  3019.  
  3020. .nseIndividuallyBlacklisted, .nseIndividuallyWhitelisted, .nseIndividuallyUntouched {
  3021. border-radius: 5px;
  3022. }
  3023.  
  3024. .nseEmoji {
  3025. display: ${nseEmojiEnabled ? 'inline-block' : 'none'};
  3026. }
  3027.  
  3028. .nseToggleHideButton {
  3029. z-index: 1 !important;
  3030. }
  3031.  
  3032. #nseRCMTitlePhraseText {
  3033. width: 95%;
  3034. }
  3035.  
  3036. .nseSettingsControlDiv {
  3037. text-align: center;
  3038. }
  3039.  
  3040. #nseFloatyBoi {
  3041. bottom: 2em;
  3042. right: 2em;
  3043. background-color: ${themes[nseSelectedTheme].backgroundColor} !important;
  3044. color: ${themes[nseSelectedTheme].foregroundColor} !important;
  3045. padding: 0.4em;
  3046. border-radius: 0.8em;
  3047. position: fixed;
  3048. float: right;
  3049. z-index: 1000;
  3050. user-select: none;
  3051. cursor: pointer;
  3052. }
  3053.  
  3054. ${nseCustomCSSEnabled ? nseCustomCSS : ''}
  3055.  
  3056. `);
  3057. // End CSS section