FA Content Filter

Filters user-defined content while browsing Furaffinity.

当前为 2020-01-10 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name FA Content Filter
  3. // @namespace fa-filter
  4. // @description Filters user-defined content while browsing Furaffinity.
  5. // @include *://www.furaffinity.net/*
  6. // @require http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
  7. // @version 1.6.0
  8. // @grant GM.getValue
  9. // @grant GM.setValue
  10. // @grant GM.deleteValue
  11. // @grant GM.openInTab
  12. // ==/UserScript==
  13.  
  14. // === WARNING ===
  15. // THE TAG FUNCTIONS ARE COMMENTED OUT IN ORDER TO PREVENT ACCIDENTAL DDoS DETECTION ON FURAFFINITY.
  16. this.$ = this.jQuery = jQuery.noConflict(true);
  17.  
  18. // Shitty workaround, but w/e
  19. async function main() {
  20. // === INITIALIZE USER ARRAY ===
  21. var userArray = JSON.parse(await GM.getValue('userList', '{}'));
  22. var versionNumber = 1.6;
  23. //var tagArray = JSON.parse(GM.getvalue('tagList', '{}'));
  24.  
  25. // === GENERAL TEMPORARY VARIABLES ===
  26. var filterEnabled = {['subs']:true, ['shouts']:true, ['coms']:true, ['notifications']:true};
  27.  
  28. // === FILTER ===
  29. var parseSettings = function() {
  30. if (!(userArray instanceof Array)) {
  31. $.each(userArray, function(username, data) {
  32. if (data['subs'] === 1) { hideSubmissions(username); }
  33. if (data['shouts'] === 1) { hideShouts(username); }
  34. if (data['coms'] === 1) { hideComments(username); }
  35. if (data['notifications'] === 1) { hideNotifications(username); }
  36. });
  37. }
  38. };
  39.  
  40. //var parseTagSettings = function() {
  41. // $('.t-image a[href^="/view"]').each(function() {
  42. // var url = $(this).attr('href');
  43. // console.log(url);
  44. // $.post(url, function(data) {
  45. // console.log($('#keywords', data).text());
  46. // });
  47. // });
  48. //}
  49.  
  50.  
  51. // === SAVE ===
  52. function writeSettings() {
  53. GM.setValue('userList', JSON.stringify(userArray));
  54. GM.setValue('versionNumber', versionNumber);
  55. }
  56.  
  57. // === FUNCTIONS ===
  58. // Hide user submissions
  59. function hideSubmissions(username) {
  60. if (isBeta()) {
  61. // Beta
  62. var submissionBeta = $('figure.u-' + escapeUsername(username));
  63. var submissionFavesBeta = $('figure[data-user="u-' + escapeUsername(username) + '"]');
  64. var submissionInboxBeta = $('a[href="/user/' + username + '"]').closest('figure');
  65.  
  66. stylizeHidden(submissionBeta);
  67. stylizeHidden(submissionFavesBeta);
  68. stylizeHidden(submissionInboxBeta);
  69.  
  70. submissionBeta.addClass('hidden-sub').hide();
  71. submissionFavesBeta.addClass('hidden-sub').hide();
  72. submissionInboxBeta.find('input').prop('checked', true);
  73. submissionInboxBeta.addClass('hidden-sub').hide();
  74.  
  75. if (!filterEnabled['subs']) {
  76. submissionBeta.show();
  77. submissionInboxBeta.show();
  78. }
  79. } else {
  80. // Classic
  81. // Browse/Submissions
  82. var submission1 = $('b[id^="sid_"] a[href="/user/' + username + '/"]').closest('b');
  83. stylizeHidden(submission1);
  84. // Mark Submissions as Checked
  85. submission1.children('small').children('input').prop('checked', true);
  86. submission1.addClass('hidden-sub').hide();
  87.  
  88. // Favorites/Front Page
  89. var submission2 = $('b[id^="sid_"] img[src$="#' + username + '"]').closest('b');
  90. stylizeHidden(submission2);
  91. submission2.addClass('hidden-sub').hide();
  92.  
  93. // Correspond to UI
  94. if (!filterEnabled['subs']) {
  95. submission1.show();
  96. submission2.show();
  97. }
  98. }
  99. }
  100.  
  101. function showSubmissions(username) {
  102. // Browse/Submissions
  103. var submission1 = $('b[id^="sid_"] a[href="/user/' + username + '/"]').closest('b');
  104. var submissionBeta = $('figure.u-' + escapeUsername(username));
  105. var submissionFavesBeta = $('figure[data-user="u-' + escapeUsername(username) + '"]');
  106. var submissionInboxBeta = $('a[href^="/user/' + username + '"]').closest('figure');
  107.  
  108. undoStylize(submission1);
  109. undoStylize(submissionBeta);
  110. undoStylize(submissionFavesBeta);
  111. undoStylize(submissionInboxBeta);
  112.  
  113. // Mark Submissions as Checked
  114. submission1.children('small').children('input').prop('checked', false);
  115. submission1.removeClass('hidden-sub').show();
  116. submissionBeta.removeClass('hidden-sub').show();
  117. submissionFavesBeta.removeClass('hidden-sub').show();
  118. submissionInboxBeta.removeClass('hidden-sub').show();
  119. submissionInboxBeta.find('input').prop('checked', false);
  120.  
  121. // Favorites/Front Page
  122. var submission2 = $('b[id^="sid_"] img[src$="#' + username + '"]').closest('b');
  123. undoStylize(submission2);
  124. submission2.removeClass('hidden-sub').show();
  125. }
  126.  
  127. // Hide user shouts
  128. function hideShouts(username) {
  129. // Classic
  130. var shout = $('table[id^="shout-"] td.alt1 img[alt="' + username + '"]').closest('table[id^="shout-"]');
  131. shout.addClass('hidden-shout').hide();
  132. stylizeHidden(shout.find('table'));
  133. shout.next('br').addClass('hidden-shout-br').hide();
  134.  
  135. // Beta
  136. var shoutBeta = $('.comment_container .shout-avatar img[alt="' + username + '"]').closest('.comment_container');
  137. shoutBeta.addClass('hidden-shout').hide();
  138. stylizeHidden(shoutBeta.find('.header'));
  139. stylizeHidden(shoutBeta.find('.body'));
  140.  
  141. // We want to only highlight and check
  142. var shoutManageBeta = $('table[id^="shout-"] .comments-flex-item-icon img[alt="' + username +'"]').closest('table[id^="shout-"]');
  143. shoutManageBeta.addClass('hidden-shout');
  144. stylizeHidden(shoutManageBeta.find('.comments-userline-flex'));
  145. stylizeHidden(shoutManageBeta.find('.comment_text'));
  146. shoutManageBeta.find('input[type="checkbox"]').prop('checked', true);
  147. }
  148.  
  149. // Hide user comments and threads
  150. function hideComments(username) {
  151. // Classic
  152. var comments = $('.container-comment td.icon img[alt="' + username + '"]').closest('.container-comment');
  153.  
  154. $(comments).each(function() {
  155. // Hide comment and get width
  156. if (!($(this).hasClass('hidden-comment'))) {
  157. var width = Number($(this).addClass('hidden-comment').hide().attr('width').slice(0,-1));
  158. var current = $(this).next('.container-comment');
  159.  
  160. // Iterate through comments until there's a width that is greater than or equal
  161. while (true) {
  162. if (current.length) {
  163. if (Number(current.attr('width').slice(0,-1)) < width) {
  164. current.addClass('hidden-comment').hide();
  165. current = current.next('.container-comment');
  166. } else {
  167. break;
  168. }
  169. } else {
  170. break;
  171. }
  172. }
  173. }
  174. });
  175.  
  176. // Beta
  177. var commentsBeta = $('.comments-list .comment_container .avatar-desktop img[alt="' + username + '"], .comments-journal .comment_container .avatar-desktop img[alt="' + username + '"]').closest('.comment_container');
  178. stylizeHidden(commentsBeta.find('.header'));
  179. stylizeHidden(commentsBeta.find('.body'));
  180.  
  181. $(commentsBeta).each(function() {
  182. // Get width, then hide comment
  183. if (!($(this).hasClass('hidden-comment'))) {
  184. var width = $(this).width();
  185. var current = $(this).next('.comment_container');
  186.  
  187. $(this).addClass('hidden-comment').hide();
  188.  
  189. // Iterate through the comments until there's a width that is greater than or equal
  190. while (true) {
  191. if (current.length) {
  192. if (current.width() < width) {
  193. current.addClass('hidden-comment').hide();
  194. current = current.next('.comment_container');
  195. } else {
  196. break;
  197. }
  198. } else {
  199. break;
  200. }
  201. }
  202. }
  203. });
  204. }
  205.  
  206. function showComments(username) {
  207. var comments = $('.comments-list .comment_container .avatar-desktop img[alt="' + username + '"], .comments-journal .comment_container .avatar-desktop img[alt="' + username + '"]').closest('.comment_container');
  208. undoStylize(comments.find('.header'));
  209. undoStylize(comments.find('.body'));
  210.  
  211. $(comments).each(function() {
  212. if ($(this).hasClass('hidden-comment')) {
  213. var width = $(this).width();
  214. var current = $(this).next('.comment_container');
  215.  
  216. $(this).removeClass('hidden-comment').show();
  217.  
  218. // Iterate through the comments until there's a width that is greater than or equal
  219. while (true) {
  220. if (current.length) {
  221. if (current.width() < width) {
  222. current.removeClass('hidden-comment').show();
  223. current = current.next('.comment_container');
  224. } else {
  225. break;
  226. }
  227. } else {
  228. break;
  229. }
  230. }
  231. }
  232. });
  233. }
  234.  
  235. // Hide user notifications
  236. function hideNotifications(username) {
  237. var notification = $('.message-stream a[href="/user/' + username + '/"]').closest('li');
  238. notification.addClass('hidden-notification').hide();
  239. stylizeHidden(notification);
  240. notification.children('input').prop('checked', true);
  241.  
  242. // Classic only
  243. notification.children('table').children('tbody').children('tr').children('td').children('.checkbox').children('input').prop('checked', true);
  244. }
  245.  
  246. function stylizeHidden(item) {
  247. $(item).css({'cssText': 'background-color: #FFBBBB !important'});
  248. $(item).css('color', '#FF0000');
  249. $('a:link', item).css('color', '#FF0000');
  250. $('a:visited', item).css('color', '#FF0000');
  251. }
  252.  
  253. function undoStylize(item) {
  254. $(item).css('background-color', '');
  255. $(item).css('color', '');
  256. $('a:link', item).css('color', '');
  257. $('a:visited', item).css('color', '');
  258. }
  259.  
  260. // === UI ===
  261. // == Filter Toggle ==
  262. // Submissions
  263. function filtersSubs() {
  264. // Remove all pre-existing UI for soft-refresh
  265. $('[id="faf-toggle-subs"]').remove();
  266. $('.faf-remove-user-external').parent().remove();
  267. $('.faf-add-user-external').parent().remove();
  268.  
  269. if ($('.hidden-sub').length > 0) {
  270. if (isBeta()) {
  271. // Beta
  272. $display = '<li class="lileft"><a class="top-heading" id="faf-toggle-subs" href="#!"><div class="sprite-nuke menu-space-saver hideonmobile"></div>Toggle Filtered Submissions (' + $('.hidden-sub').length + ')</a></li>';
  273. $('.lileft').last().after($display);
  274. } else {
  275. // Classic
  276. // $display = '<input id="faf-toggle-subs" class="button" type="button" value="Toggle Filtered Submissions (' + $('.hidden-sub').length + ')"></input>';
  277. // $('form[name="replyform"]').first().append($display);
  278. $display = '<li><a id="faf-toggle-subs" href="#!">⚠ Toggle Filtered Submissions (' + $('.hidden-sub').length + ')</a></li>';
  279. $('.search-box-container').first().before($display);
  280. }
  281. } else {
  282. filterEnabled['subs'] = true;
  283. }
  284.  
  285. if (isBeta()) {
  286. // Beta
  287. $('figure').each(function() {
  288. var username = $(this).attr('class').match('u-([^\\s]+)');
  289. if (!username) {
  290. username = $(this).attr('data-user').match('u-([^\\s]+)');
  291. }
  292. if (username) {
  293. username = username[1];
  294. if (username in userArray && userArray[username]['subs'] === 1) {
  295. $(this).find('figcaption').append('<p><a style="color: #FF5555!important;" class="faf-remove-user-external faf-ex-subs" id="faf-' + username + '" href="#!" title="Remove ' + username + ' from filter">[Unfilter]</a></p>');
  296. } else {
  297. $(this).find('figcaption').append('<p><a style="color: #FF5555!important;" class="faf-add-user-external faf-ex-subs" id="faf-' + username + '" href="#!" title="Add ' + username + ' to filter">[Filter]</a></p>');
  298. }
  299. }
  300. });
  301. } else {
  302. $('b[id^="sid_"]').each(function() {
  303. var username = $(this).find('small a').attr('href');
  304. username = username.match('/user/(.*)/');
  305. if (username) {
  306. if (username[1] in userArray && userArray[username[1]]['subs'] === 1) {
  307. $(this).find('small').append('<span>&nbsp;<a style="color: #FF5555!important;" class="faf-remove-user-external faf-ex-subs" id="faf-' + username[1] + '" href="#!" title="Remove ' + username[1] + ' from filter">[Unfilter]</a></span>');
  308. } else {
  309. $(this).find('small').append('<span>&nbsp;<a style="color: #FF5555!important;" class="faf-add-user-external faf-ex-subs" id="faf-' + username[1] + '" href="#!" title="Add ' + username[1] + ' to filter">[Filter]</a></span>');
  310. }
  311. }
  312. });
  313. }
  314. }
  315.  
  316. // Followed Submissions
  317. function filtersSubsFollow() {
  318. if ($('.hidden-sub').length > 0) {
  319. if (isBeta()) {
  320. $display = '<div class="button-nav-item"><button class="button mobile-button" id="faf-toggle-subs" type="button">Toggle Filtered Submissions (' + $('.hidden-sub').length + ')</button></div>';
  321. $('.actions').css('max-width', '700px');
  322. } else {
  323. $display = '<input id="faf-toggle-subs" class="button" type="button" value="Toggle Filtered Submissions (' + $('.hidden-sub').length + ')"></input>';
  324. }
  325. $('.actions').append($display);
  326. }
  327. }
  328.  
  329. // Shouts
  330. function filtersShouts() {
  331. if ($('.hidden-shout').length > 0) {
  332. $display = '<input id="faf-toggle-shouts" class="button" type="button" value="Toggle Filtered Shouts (' + $('.hidden-shout').length + ')"></input>';
  333. // Classic
  334. $('table[id^="shout-"]').first().prevAll('table.maintable:first').append($display);
  335. // Beta
  336. $($display).insertBefore($('.section-body .comment_container').first());
  337. }
  338. }
  339.  
  340. // Shouts (Controls, Beta Only)
  341. function filtersShoutsControl() {
  342. if ($('.hidden-shout').length > 0) {
  343. $display = '<button id="faf-toggle-shouts" class="button mobile-button" type="button" value="Toggle Filtered Shouts (' + $('.hidden-shout').length + ')">Toggle Filtered Shouts (' + $('.hidden-shout').length + ')</button>';
  344. $('.section-divider').last().append($display);
  345. $('.hidden-shout input').prop('checked', true);
  346. }
  347. }
  348.  
  349. // Comments
  350. function filtersComments() {
  351. // Remove all pre-existing UI for soft-refresh
  352. $('[id="faf-toggle-comments"]').remove();
  353. $('.faf-remove-user-external').parent().remove();
  354. $('.faf-add-user-external').parent().remove();
  355.  
  356. if ($('.hidden-comment').length > 0) {
  357. $display = '<input style="float:right;" id="faf-toggle-comments" class="button" type="button" value="Toggle Filtered Comments (' + $('.hidden-comment').length + ')"></input>';
  358. if (isBeta()) {
  359. // Beta
  360. $($display).insertBefore('#comments-submission, #responsebox');
  361. } else {
  362. // Classic
  363. $('table.container-comment').first().parent().parent().prev().children().append($display);
  364. }
  365. }
  366.  
  367. $('.comments-list .comment_container, .comments-journal .comment_container').each(function() {
  368. var username = $(this).find('.avatar-desktop img').attr('alt');
  369. if (username) {
  370. if (username in userArray && userArray[username]['coms'] === 1) {
  371. $(this).find('.comment-date').append('<span>&nbsp;<a style="color: #FF5555!important;" class="faf-remove-user-external faf-ex-coms" id="faf-' + username + '" href="#!" title="Show ' + username + '\'s comments">[Unfilter]</a></span>');
  372. } else {
  373. $(this).find('.comment-date').append('<span>&nbsp;<a style="color: #FF5555!important;" class="faf-add-user-external faf-ex-coms" id="faf-' + username + '" href="#!" title="Hide ' + username + '\'s comments">[Filter]</a></span>');
  374. }
  375. }
  376. });
  377. }
  378.  
  379. // Notifications
  380. function filtersNotifications() {
  381. if ($('.hidden-notification').length > 0) {
  382. $display = '<input id="faf-toggle-notifications" class="button" type="button" value="Toggle Filtered Notifications (' + $('.hidden-notification').length + ')"></input>';
  383. $('.global-controls').append($display);
  384. $('.global_controls').append($display);
  385.  
  386. // = Notification Count =
  387. // Classic
  388. if ($('fieldset[id^="messages-watches"] .hidden-notification').length > 0)
  389. $('fieldset[id^="messages-watches"] h3').append(' (' + $('fieldset[id^="messages-watches"] .hidden-notification').length + ' filtered)');
  390. if ($('fieldset[id^="messages-comments-submission"] .hidden-notification').length > 0)
  391. $('fieldset[id^="messages-comments-submission"] h3').append(' (' + $('fieldset[id^="messages-comments-submission"] .hidden-notification').length + ' filtered)');
  392. if ($('fieldset[id^="messages-shouts"] .hidden-notification').length > 0)
  393. $('fieldset[id^="messages-shouts"] h3').append(' (' + $('fieldset[id^="messages-shouts"] .hidden-notification').length + ' filtered)');
  394. if ($('fieldset[id^="messages-favorites"] .hidden-notification').length > 0)
  395. $('fieldset[id^="messages-favorites"] h3').append(' (' + $('fieldset[id^="messages-favorites"] .hidden-notification').length + ' filtered)');
  396.  
  397. // Beta
  398. if ($('div[id^="messages-watches"] .hidden-notification').length > 0)
  399. $('div[id^="messages-watches"] h2').append(' (' + $('div[id^="messages-watches"] .hidden-notification').length + ' filtered)');
  400. if ($('div[id^="messages-comments-submission"] .hidden-notification').length > 0)
  401. $('div[id^="messages-comments-submission"] h2').append(' (' + $('div[id^="messages-comments-submission"] .hidden-notification').length + ' filtered)');
  402. if ($('div[id^="messages-shouts"] .hidden-notification').length > 0)
  403. $('div[id^="messages-shouts"] h2').append(' (' + $('div[id^="messages-shouts"] .hidden-notification').length + ' filtered)');
  404. if ($('div[id^="messages-favorites"] .hidden-notification').length > 0)
  405. $('div[id^="messages-favorites"] h2').append(' (' + $('div[id^="messages-favorites"] .hidden-notification').length + ' filtered)');
  406. if ($('div[id^="messages-journals"] .hidden-notification').length > 0)
  407. $('div[id^="messages-journals"] h2').append(' (' + $('div[id^="messages-journals"] .hidden-notification').length + ' filtered)');
  408. }
  409. }
  410.  
  411. // == Buttons ==
  412. // Show/Hide Submissions
  413. $(document.body).on('click', '#faf-toggle-subs', function() {
  414. $('.hidden-sub').toggle();
  415. filterEnabled['subs'] = !filterEnabled['subs'];
  416. });
  417.  
  418. // Show/Hide Shouts
  419. $(document.body).on('click', '#faf-toggle-shouts', function() {
  420. $('.hidden-shout').toggle();
  421. $('.hidden-shout-br').toggle();
  422. filterEnabled['shouts'] = !filterEnabled['shouts'];
  423. });
  424.  
  425. // Show/Hide Comments
  426. $(document.body).on('click', '#faf-toggle-comments', function() {
  427. $('.hidden-comment').toggle();
  428. filterEnabled['coms'] = !filterEnabled['coms'];
  429. });
  430.  
  431. // Show/Hide Notifications
  432. $(document.body).on('click', '#faf-toggle-notifications', function() {
  433. $('.hidden-notification').toggle();
  434. filterEnabled['notifications'] = !filterEnabled['notifications'];
  435. });
  436.  
  437. // == External Filters ==
  438. // Add submission filter outside of settings
  439. $(document.body).on('click', '.faf-add-user-external', function() {
  440. var addUser = $(this).attr('id').match('faf-(.*)')[1];
  441.  
  442. // Add to array
  443. if (!(addUser in userArray)) {
  444. userArray[addUser] = {
  445. 'subs': $(this).hasClass('faf-ex-subs') ? 1 : 0,
  446. 'shouts': $(this).hasClass('faf-ex-shouts') ? 1 : 0,
  447. 'coms': $(this).hasClass('faf-ex-coms') ? 1 : 0,
  448. 'notifications': $(this).hasClass('faf-ex-notifications') ? 1 : 0
  449. };
  450. } else {
  451. if ($(this).hasClass('faf-ex-subs')) {
  452. userArray[addUser]['subs'] = 1;
  453. } else if ($(this).hasClass('faf-ex-coms')) {
  454. userArray[addUser]['coms'] = 1;
  455. }
  456. }
  457.  
  458. // Hide, replace link, and save
  459. if ($(this).hasClass('faf-ex-subs')) {
  460. hideSubmissions(addUser);
  461. filtersSubs();
  462. } else if ($(this).hasClass('faf-ex-coms')) {
  463. hideComments(addUser);
  464. filtersComments();
  465. }
  466. writeSettings();
  467. });
  468.  
  469. // Remove submission filter outside of settings
  470. $(document.body).on('click', '.faf-remove-user-external', function() {
  471. var removeUser = $(this).attr('id').match('faf-(.*)')[1];
  472.  
  473. // Remove from array
  474. if (removeUser in userArray) {
  475. if ($(this).hasClass('faf-ex-subs')) {
  476. userArray[removeUser]['subs'] = 0;
  477. } else if ($(this).hasClass('faf-ex-coms')) {
  478. userArray[removeUser]['coms'] = 0;
  479. }
  480. }
  481.  
  482. // Show, replace link, and save
  483. if ($(this).hasClass('faf-ex-subs')) {
  484. showSubmissions(removeUser);
  485. filtersSubs();
  486. } else if ($(this).hasClass('faf-ex-coms')) {
  487. showComments(removeUser);
  488. filtersComments();
  489. }
  490. writeSettings();
  491. });
  492.  
  493. // == User Settings ==
  494. function displaySettings() {
  495. // Navbar link
  496. $('<li class="noblock"><a target="_blank" href="/controls/site-settings#fa-filter">FA Filter</a></li>').insertAfter($('.navhideonmobile li').last());
  497. // Navbar link (Classic)
  498. $('<li class="noblock"><a target="_blank" href="/controls/site-settings#fa-filter">FA Filter</a></li>').insertAfter($('li#sfw-toggle'));
  499.  
  500. if (window.location.pathname.lastIndexOf('/controls/site-settings', 0) === 0) {
  501. if (isBeta()) {
  502. // Beta HTML Code
  503. var settingsDisplay = '<section>' +
  504. '<div class="section-body">' +
  505. '<h2 id="fa-filter">FA Filter</h2>' +
  506. '<div class="control-panel-item-container">' +
  507. '<div class="control-panel-item-name"><h4>Add a User</h4></div>' +
  508. '<div class="control-panel-item-description">' +
  509. '<p>Tired of seeing somebody\'s contributions on the site? Add them to your filter list!<br/>Enter in the username of the person you want to filter, which is the username that would appear after "furaffinity.net/user/".</p>' +
  510. '</div>' +
  511. '<div class="control-panel-item-options">' +
  512. '<input class="textbox" type="text" id="faf-add-username" maxlength="50"></input>&nbsp;&nbsp;&nbsp;<input id="faf-add" class="button" type="button" value="Add" />' +
  513. '</div>' +
  514. '</div>' +
  515. '<div class="control-panel-item-container">' +
  516. '<div class="control-panel-item-name"><h4>Validate Filters</h4></div>' +
  517. '<div class="control-panel-item-description">' +
  518. '<p>This double-checks to make sure that your filtered usernames are correct and, optionally, removes users that don\'t have any enabled filters. This automatically saves the list.</p>' +
  519. '<strong class="highlight">Validate Only</strong> - Simply validates that the usernames that have been entered are correctly formatted.<br/>' +
  520. '<strong class="highlight">Validate and Remove Unused</strong> - Does the same as above and also removes any users with zero filters in the list.<br/>' +
  521. '</div>' +
  522. '<div class="control-panel-item-options">' +
  523. '<select name="faf-validate-options" id="select-faf-validate-options" class="styled">' +
  524. '<option value="v" selected="selected">Validate Only</option>' +
  525. '<option value="vr">Validate and Remove Unused</option>' +
  526. '</select><br/><input id="faf-validate" class="button" type="button" value="Apply" style="margin-top: 10px;"/><br/>' +
  527. '<span class="faf-validate-status" style="font-weight: bold; color: #009900; display: none;">Validated! 0 user(s) have been modified or removed.</span>' +
  528. '</div>' +
  529. '</div>' +
  530. '<div class="control-panel-item-container">' +
  531. '<div class="control-panel-item-name"><h4>Export/Import Data</h4></div>' +
  532. '<div class="control-panel-item-description">' +
  533. '<p>Export your filters or import them from somewhere else.</p>' +
  534. '</div>' +
  535. '<div class="control-panel-item-options">' +
  536. '<input class="textbox" type="text" id="faf-raw-port" style="margin-bottom: 10px; width: 100%;" placeholder="Paste your filter data here..."></input><br/>' +
  537. '<input id="faf-port-clear" class="button" type="button" value="Clear" />&nbsp;&nbsp;&nbsp;<input id="faf-import" class="button" type="button" value="Import" />&nbsp;&nbsp;&nbsp;<input id="faf-export" class="button" type="button" value="Export" /><br/>' +
  538. '<span class="faf-import-status" style="font-weight: bold; color: #FF6666; display: none;">Invalid data!</span>' +
  539. '</div>' +
  540. '</div>' +
  541. '<div class="activity-periods-list">' +
  542. '<table class="container faf-list faf-list-beta" width="100%" cellspacing="0" cellpadding="0" border="0" style="padding:0 15px 10px 15px">' +
  543. '<tbody>' +
  544. '<tr>' +
  545. '<td class="p10t p5r p5b"><h3>Username</h3></td>' +
  546. '<td class="p10t p5r p5b" width="200px"><h3>Submissions</h3></td>' +
  547. '<td class="p10t p5r p5b" width="200px"><h3>Shouts</h3></td>' +
  548. '<td class="p10t p5r p5b" width="200px"><h3>Comments</h3></td>' +
  549. '<td class="p10t p5r p5b" width="200px"><h3>Notifications</h3></td>' +
  550. '</tr>' +
  551. '</tbody>' +
  552. '</table>' +
  553. '</div>' +
  554. '<div class="section-options">' +
  555. '<span class="faf-update-status" style="font-weight: bold; color: #006600; display: none;">Update successful!</span>&nbsp;&nbsp;<input class="button mobile-button" id="faf-update" type="button" value="Apply Filters (FA Filter)">' +
  556. '</div>' +
  557. '</div>' +
  558. '</section>';
  559. $(settingsDisplay).insertBefore($('section').last());
  560. } else {
  561. // Classic HTML Code
  562. var settingsDisplay = '<table id="fa-filter" cellpadding="0" cellspacing="1" border="0" class="section maintable"><tbody>' +
  563. '<tr><td height="22" class="cat links">&nbsp;<strong>FA Filter</strong></td></tr>' +
  564. '<tr><td class="alt1 addpad ucp-site-settings" align="center">' +
  565. '<table cellspacing="1" cellpadding="0" border="0"><tbody>' +
  566. '<tr>' +
  567. '<th><strong>Add a User</strong></th>' +
  568. '<td><input type="text" id="faf-add-username" maxlength="50"></input>&nbsp;<input id="faf-add" class="button" type="button" value="Add User"></td>' +
  569. '<td class="option-description">' +
  570. '<h3>Hide a user\'s contributions to the site.</h3>' +
  571. '<p>Tired of seeing somebody\'s contributions on the site? Add them to your filter list!<br>Note: Enter in the username of the person you want to filter, which is the username that would appear after "furaffinity.net/user/".</p>' +
  572. '</td>' +
  573. '</tr>' +
  574. '<tr>' +
  575. '<th><strong>Validate Filters</strong></th>' +
  576. '<td>' +
  577. '<select name="faf-validate-options" id="select-faf-validate-options" class="styled">' +
  578. '<option value="v" selected="selected">Vaildate Filters Only</option>' +
  579. '<option value="vr">Validate and Remove Unused Filters</option>' +
  580. '</select>&nbsp;<input id="faf-validate" class="button" type="button" value="Apply" /><br/>' +
  581. '<span class="faf-validate-status" style="font-weight: bold; color: #009900; display: none;">Validated! 0 user(s) have been modified or removed.</span>' +
  582. '</td>' +
  583. '<td class="option-description">' +
  584. '<h3>Clean up everything and revalidate filtered usernames.</h3>' +
  585. '<p>This double-checks to make sure that your filtered usernames are correct and, optionally, removes users that don\'t have any enabled filters.<br/><strong>Note:</strong> This automatically saves the list.</p>' +
  586. '</td>' +
  587. '</tr>' +
  588. '<tr>' +
  589. '<th class="noborder" style="vertical-align: text-top;"><strong style="position: relative; top: 25px;">Modify Filters</strong></th>' +
  590. '<td class="noborder">' +
  591. '<table cellspacing="0" cellpadding="0" border="0" class="faf-list faf-list-classic">' +
  592. '<tr><th><strong>Username</strong></th><th><strong>Submissions</strong></th><th><strong>Shouts</strong></th><th><strong>Comments</strong></th><th><strong>Notifications</strong></th></tr>' +
  593. '</table>' +
  594. '<br><br><input class="button" id="faf-update" type="button" value="Apply Filters (FA Filter)"> <span class="faf-update-status" style="font-weight: bold; color: #006600; display: none;">Update successful!</span>' +
  595. '</td>' +
  596. '<td class="option-description noborder">' +
  597. '<h3>Choose what items you don\'t want to see.</h3>' +
  598. '<p>If you still want to see some of the things that a user contributes, you can control that here.</p>' +
  599. '</td>' +
  600. '</tr>' +
  601. '</tbody></table>' +
  602. '</td></tr>' +
  603. '</tbody></table>';
  604. $('form').last().append(settingsDisplay);
  605. }
  606.  
  607. // Populate list
  608. $.each(userArray, function(username, data) {
  609. addFilterUser(username, data);
  610. });
  611. }
  612. }
  613.  
  614. // Display user in the filter table
  615. function addFilterUser(username, data) {
  616. if (isBeta()) {
  617. // Beta
  618. var rowBeta = '<tr class="faf-filter-table-row" id="filter-' + username + '"><td class="p5r" valign="middle" width="auto"><a class="faf-remove" id="faf-rm-' + username + '" href="#!">[x]</a> ' + username + '</td>';
  619. if (data['subs'] === 1) { rowBeta += '<td class="p5r" valign="middle" width="auto"><input id="faf-check-subs-' + username + '" type="checkbox" checked="checked"></td>'; } else { rowBeta += '<td class="p5r" valign="middle" width="auto"><input id="faf-check-subs-' + username + '" type="checkbox"></td>'; }
  620. if (data['shouts'] === 1) { rowBeta += '<td class="p5r" valign="middle" width="auto"><input id="faf-check-shouts-' + username + '" type="checkbox" checked="checked"></td>'; } else { rowBeta += '<td class="p5r" valign="middle" width="auto"><input id="faf-check-shouts-' + username + '" type="checkbox"></td>'; }
  621. if (data['coms'] === 1) { rowBeta += '<td class="p5r" valign="middle" width="auto"><input id="faf-check-coms-' + username + '" type="checkbox" checked="checked"></td>'; } else { rowBeta += '<td class="p5r" valign="middle" width="auto"><input id="faf-check-coms-' + username + '" type="checkbox"></td>'; }
  622. if (data['notifications'] === 1) { rowBeta += '<td class="p5r" valign="middle" width="auto"><input id="faf-check-notifications-' + username + '" type="checkbox" checked="checked"></td>'; } else { rowBeta += '<td class="p5r" valign="middle" width="auto"><input id="faf-check-notifications-' + username + '" type="checkbox"></td>'; }
  623.  
  624. rowBeta += '</tr>';
  625.  
  626. $('table.faf-list tr:last').after(rowBeta);
  627. } else {
  628. // Classic
  629. var row = '<tr class="checked" id="filter-' + username + '"><td class="noborder"><a class="faf-remove fonthighlight" id="faf-rm-' + username + '" href="#!">[x]</a> ' + username + '</td>';
  630. if (data['subs'] === 1) { row += '<td class="noborder"><input id="faf-check-subs-' + username + '" type="checkbox" checked="checked"></td>'; } else { row += '<td class="noborder"><input id="faf-check-subs-' + username + '" type="checkbox"></td>'; }
  631. if (data['shouts'] === 1) { row += '<td class="noborder"><input id="faf-check-shouts-' + username + '" type="checkbox" checked="checked"></td>'; } else { row += '<td class="noborder"><input id="faf-check-shouts-' + username + '" type="checkbox"></td>'; }
  632. if (data['coms'] === 1) { row += '<td class="noborder"><input id="faf-check-coms-' + username + '" type="checkbox" checked="checked"></td>'; } else { row += '<td class="noborder"><input id="faf-check-coms-' + username + '" type="checkbox"></td>'; }
  633. if (data['notifications'] === 1) { row += '<td class="noborder"><input id="faf-check-notifications-' + username + '" type="checkbox" checked="checked"></td>'; } else { row += '<td class="noborder"><input id="faf-check-notifications-' + username + '" type="checkbox"></td>'; }
  634.  
  635. row += '</tr>';
  636.  
  637. $('table.faf-list tr:last').after(row);
  638. }
  639. }
  640.  
  641. // Add
  642. $(document.body).on('click', '#faf-add', function() {
  643. var username = $.trim($('#faf-add-username').val());
  644. $('#faf-add-username').val('');
  645. if (username !== '') {
  646. username = username.toLowerCase();
  647. username = username.replace(/[_]/g, '');
  648. if (!(username in userArray)) {
  649. userArray[username] = {'subs':1, 'shouts':1, 'coms':1, 'notifications':1};
  650. addFilterUser(username, userArray[username]);
  651. }
  652. }
  653. });
  654.  
  655. // Remove
  656. $(document.body).on('click', 'a.faf-remove', function(event) {
  657. var username = event.target.id.substr(7);
  658. delete userArray[username];
  659.  
  660. userEsc = escapeUsername(username);
  661.  
  662. $('table.faf-list tr#filter-' + userEsc).remove();
  663. });
  664.  
  665. // Update
  666. $(document.body).on('click', '#faf-update', function() {
  667. $('.faf-list tr[id^="filter-"]').each(function() {
  668. var username = this.id.substr(7);
  669. var vals = {'subs':0, 'shouts':0, 'coms':0, 'notifications':0};
  670.  
  671. userEsc = escapeUsername(username);
  672.  
  673. // Check checkboxes
  674. if ($('#faf-check-subs-' + userEsc).is(':checked')) { vals['subs'] = 1; }
  675. if ($('#faf-check-shouts-' + userEsc).is(':checked')) { vals['shouts'] = 1; }
  676. if ($('#faf-check-coms-' + userEsc).is(':checked')) { vals['coms'] = 1; }
  677. if ($('#faf-check-notifications-' + userEsc).is(':checked')) { vals['notifications'] = 1; }
  678.  
  679. userArray[username] = vals;
  680. });
  681.  
  682. // Save
  683. writeSettings();
  684.  
  685. // Display message
  686. $('.faf-update-status').fadeIn('slow');
  687. setTimeout(function() {
  688. $('.faf-update-status').fadeOut('slow');
  689. }, 5000);
  690. });
  691.  
  692. // Validate
  693. $(document.body).on('click', '#faf-validate', function() {
  694. var modCount = 0;
  695. // Validate
  696. $.each(userArray, function(username, data) {
  697. var tempUsername = username;
  698. tempUsername = tempUsername.trim();
  699. if (tempUsername !== '') {
  700. tempUsername = tempUsername.toLowerCase();
  701. tempUsername = tempUsername.replace(/[_ ]/g, '');
  702. if (tempUsername !== username) {
  703. userArray[tempUsername] = data;
  704. delete userArray[username];
  705. $('tr[id="filter-' + username + '"]').remove();
  706. modCount++;
  707. }
  708. }
  709. });
  710.  
  711. // Remove empty
  712. if ($('#select-faf-validate-options').val() === 'vr') {
  713. $.each(userArray, function(username, data) {
  714. var isEmpty = true;
  715. $.each(data, function(entity, value) {
  716. if (value === 1) {
  717. isEmpty = false;
  718. }
  719. });
  720. if (isEmpty) {
  721. delete userArray[username];
  722. $('tr[id="filter-' + username + '"]').remove();
  723. modCount++;
  724. }
  725. });
  726. }
  727.  
  728. // Save
  729. writeSettings();
  730.  
  731. // Display message
  732. $('.faf-validate-status').text('Validated! ' + modCount + ' user(s) have been modified or removed.');
  733. $('.faf-validate-status').fadeIn('slow');
  734. setTimeout(function() {
  735. $('.faf-validate-status').fadeOut('slow');
  736. }, 5000);
  737. });
  738.  
  739. // IMPORT/EXPORT
  740. // Clear
  741. $(document.body).on('click', '#faf-port-clear', function() {
  742. $('#faf-raw-port').val('');
  743. });
  744.  
  745. // Export
  746. $(document.body).on('click', '#faf-export', function() {
  747. var exportVal = {'versionNumber': versionNumber, 'userList': userArray};
  748. $('#faf-raw-port').val(JSON.stringify(exportVal));
  749. });
  750.  
  751. // Import
  752. $(document.body).on('click', '#faf-import', async function() {
  753. if ($.trim($('#faf-raw-port').val()).length > 0) {
  754. var importJson = null;
  755. try {
  756. importJson = JSON.parse($('#faf-raw-port').val());
  757.  
  758. await validateAndImportData(importJson);
  759.  
  760. writeSettings();
  761.  
  762. $('.faf-import-status').css('color', '#006600').text('Import successful!');
  763. $('.faf-import-status').fadeIn('slow');
  764. setTimeout(function() {
  765. $('.faf-import-status').fadeOut('slow');
  766. }, 5000);
  767. } catch (e) {
  768. $('.faf-import-status').css('color', '#FF6666').text('Invalid data!');
  769. $('.faf-import-status').fadeIn('slow');
  770. setTimeout(function() {
  771. $('.faf-import-status').fadeOut('slow');
  772. }, 5000);
  773. return;
  774. }
  775. }
  776. });
  777.  
  778. // === UTILITIES ===
  779. function escapeUsername(username) {
  780. // Replace periods/colons/tildes with escaped versions. Who the fuck allows periods AND tildes in usernames, seriously?
  781. userEsc = username.replace(/\./g, '\\.');
  782. userEsc = userEsc.replace(/:/g, '\\:');
  783. userEsc = userEsc.replace(/~/g, '\\~');
  784. return userEsc;
  785. }
  786.  
  787. function updateCSS() {
  788. // Fuck Greasemonkey
  789. var newCSS = '<style type="text/css">' +
  790. 'section.gallery figure { padding-bottom: 62px; }' +
  791. '.faf-filter-table-row:hover { background-color: #888888; }' +
  792. '</style>';
  793. $('head').append(newCSS);
  794. }
  795.  
  796. function isBeta() {
  797. return $('body').attr('data-static-path') === '/themes/beta';
  798. }
  799.  
  800. // IMPORT FUNCTIONALITY - UPDATE WITH EACH MAJOR UPDATE
  801. async function validateAndImportData(jsonData) {
  802. switch (jsonData.version) {
  803. case '1.6':
  804. default:
  805. // Version 1.6 - User data only
  806. $.each(jsonData.userList, function(user, filters) {
  807. // Validate each user and filter
  808. $.each(filters, function(type, value) {
  809. if (value != 0 && value != 1) {
  810. throw "Invalid value.";
  811. }
  812. });
  813. userArray[user] = {
  814. 'subs': filters.subs,
  815. 'shouts': filters.shouts,
  816. 'coms': filters.coms,
  817. 'notifications': filters.notifications
  818. }
  819. });
  820. }
  821. }
  822.  
  823. displaySettings();
  824. updateCSS();
  825.  
  826. setTimeout(parseSettings, 50);
  827. //setTimeout(parseTagSettings, 100);
  828.  
  829. // Submissions
  830. if (window.location.pathname.lastIndexOf('/browse', 0) === 0) setTimeout(filtersSubs, 100);
  831. else if (window.location.pathname.lastIndexOf('/favorites', 0) === 0) setTimeout(filtersSubs, 100);
  832. else if (window.location.pathname.lastIndexOf('/msg/submissions', 0) === 0) setTimeout(filtersSubsFollow, 100);
  833. // Shouts
  834. else if (window.location.pathname.lastIndexOf('/user', 0) === 0) setTimeout(filtersShouts, 100);
  835. else if (window.location.pathname.lastIndexOf('/controls/shouts', 0) === 0) setTimeout(filtersShoutsControl, 100);
  836. // Comments
  837. else if (window.location.pathname.lastIndexOf('/view', 0) === 0) setTimeout(filtersComments, 100);
  838. else if (window.location.pathname.lastIndexOf('/journal', 0) === 0) setTimeout(filtersComments, 100);
  839. // Notifications
  840. else if (window.location.pathname.lastIndexOf('/msg/others', 0) === 0) setTimeout(filtersNotifications, 100);
  841. else setTimeout(filtersSubs, 100);
  842. }
  843.  
  844. main();