FA Content Filter

Filters user-defined content while browsing FA.

当前为 2015-12-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name FA Content Filter
  3. // @namespace fa-filter
  4. // @description Filters user-defined content while browsing FA.
  5. // @include *://www.furaffinity.net/*
  6. // @require http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
  7. // @version 1.4.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. // === INITIALIZE USER ARRAY ===
  19. var userArray = JSON.parse(GM_getValue('userList', '{}'));
  20. //var tagArray = JSON.parse(GM_getvalue('tagList', '{}'));
  21.  
  22. // === FILTER ===
  23. var parseSettings = function() {
  24. if (!(userArray instanceof Array)) {
  25. $.each(userArray, function(username, data) {
  26. if (data['subs'] === 1) { hideSubmissions(username); }
  27. if (data['shouts'] === 1) { hideShouts(username); }
  28. if (data['coms'] === 1) { hideComments(username); }
  29. if (data['notifications'] === 1) { hideNotifications(username); }
  30. });
  31. }
  32. }
  33.  
  34. //var parseTagSettings = function() {
  35. // $('.t-image a[href^="/view"]').each(function() {
  36. // var url = $(this).attr('href');
  37. // console.log(url);
  38. // $.post(url, function(data) {
  39. // console.log($('#keywords', data).text());
  40. // });
  41. // });
  42. //}
  43.  
  44.  
  45. // === SAVE ===
  46. function writeSettings() {
  47. GM_setValue('userList', JSON.stringify(userArray));
  48. }
  49.  
  50. // === FUNCTIONS ===
  51. // Hide user submissions
  52. function hideSubmissions(username) {
  53. // Browse/Submissions
  54. var submission1 = $('b[id^="sid_"] a[href="/user/' + username + '/"]').closest('b');
  55. stylizeHidden(submission1);
  56. // Mark Submissions as Checked
  57. submission1.children('small').children('input').prop('checked', true);
  58. submission1.addClass('hidden-sub').hide();
  59. // Favorites/Front Page
  60. var submission2 = $('b[id^="sid_"] img[src$="#' + username + '"]').closest('b');
  61. stylizeHidden(submission2);
  62. submission2.addClass('hidden-sub').hide();
  63. }
  64.  
  65. // Hide user shouts
  66. function hideShouts(username) {
  67. // Classic
  68. var shout = $('table[id^="shout-"] td.alt1 img[alt="' + username + '"]').closest('table[id^="shout-"]');
  69. shout.addClass('hidden-shout').hide();
  70. stylizeHidden(shout.find('table'));
  71. shout.next('br').addClass('hidden-shout-br').hide();
  72. // Beta
  73. var shoutBeta = $('table[id^="shout-"] .comments-flex-item-icon img[alt="' + username +'"]').closest('table[id^="shout-"]');
  74. shoutBeta.addClass('hidden-shout').hide();
  75. stylizeHidden(shoutBeta.find('.comments-flex-item-main'));
  76. }
  77.  
  78. // Hide user comments and threads
  79. function hideComments(username) {
  80. // Classic
  81. var comments = $('.container-comment td.icon img[alt="' + username + '"]').closest('.container-comment');
  82. $(comments).each(function() {
  83. // Hide comment and get width
  84. if (!($(this).hasClass('hidden-comment'))) {
  85. var width = Number($(this).addClass('hidden-comment').hide().attr('width').slice(0,-1));
  86. var current = $(this).next('.container-comment');
  87.  
  88. // Iterate through comments until there's a width that is greater than or equal
  89. while (true) {
  90. if (current.length) {
  91. if (Number(current.attr('width').slice(0,-1)) < width) {
  92. current.addClass('hidden-comment').hide();
  93. current = current.next('.container-comment');
  94. } else {
  95. break;
  96. }
  97. } else {
  98. break;
  99. }
  100. }
  101. }
  102. });
  103. // Beta
  104. var commentsBeta = $('.container-comment .comments-flex-item-icon img[alt="' + username + '"]').closest('.container-comment');
  105. stylizeHidden(commentsBeta.find('.comments-flex-item-main'));
  106. $(commentsBeta).each(function() {
  107. // Hide comment and get width
  108. if (!($(this).hasClass('hidden-comment'))) {
  109. var width = Number($(this).addClass('hidden-comment').hide().attr('width').slice(0,-1));
  110. var current = $(this).next('.container-comment');
  111. // Iterate through the comments until there's a width that is greater than or equal
  112. while (true) {
  113. if (current.length) {
  114. if (Number(current.attr('width').slice(0,-1)) < width) {
  115. current.addClass('hidden-comment').hide();
  116. current = current.next('.container-comment');
  117. } else {
  118. break;
  119. }
  120. } else {
  121. break;
  122. }
  123. }
  124. }
  125. });
  126. }
  127. // Hide user notifications
  128. function hideNotifications(username) {
  129. var notification = $('.message-stream a[href="/user/' + username + '/"]').closest('li');
  130. notification.addClass('hidden-notification').hide();
  131. stylizeHidden(notification);
  132. notification.children('input').prop('checked', true);
  133. // Classic only
  134. notification.children('table').children('tbody').children('tr').children('td').children('.checkbox').children('input').prop('checked', true);
  135. }
  136. function stylizeHidden(item) {
  137. item.css('background-color', '#FFBBBB');
  138. item.css('color', '#FF0000');
  139. $('a:link', item).css('color', '#FF0000');
  140. $('a:visited', item).css('color', '#FF0000');
  141. }
  142.  
  143. // === UI ===
  144. // == Filter Toggle ==
  145. // Submissions
  146. function filtersSubs() {
  147. if ($('.hidden-sub').length > 0) {
  148. $display = '<input style="float:right;" id="faf-toggle-subs" class="button" type="button" value="Toggle Filtered Submissions (' + $('.hidden-sub').length + ')"></input>';
  149. $('form').first().append($display);
  150. }
  151. }
  152.  
  153. // Followed Submissions
  154. function filtersSubsFollow() {
  155. if ($('.hidden-sub').length > 0) {
  156. $display = '<input id="faf-toggle-subs" class="button" type="button" value="Toggle Filtered Submissions (' + $('.hidden-sub').length + ')"></input>';
  157. $('.actions').append($display);
  158. }
  159. }
  160.  
  161. // Shouts
  162. function filtersShouts() {
  163. if ($('.hidden-shout').length > 0) {
  164. $display = '<center><input id="faf-toggle-shouts" class="button" type="button" value="Toggle Filtered Shouts (' + $('.hidden-shout').length + ')"></input></center>';
  165. // Classic
  166. $('table[id^="shout-"]').first().prevAll('table.maintable:first').append($display);
  167. // Beta
  168. $('.shoutboxcontainer').append($display);
  169. }
  170. }
  171.  
  172. // Shouts (Controls, Beta Only)
  173. function filtersShoutsControl() {
  174. if ($('.hidden-shout').length > 0) {
  175. $display = '<br><br><input id="faf-toggle-shouts" class="button" type="button" value = "Toggle Filtered Shouts (' + $('.hidden-shout').length + ')"></input>';
  176. $('div[id="controlpanel"] .alignright').append($display);
  177. $('.hidden-shout input').prop('checked', true);
  178. }
  179. }
  180.  
  181. // Comments
  182. function filtersComments() {
  183. if ($('.hidden-comment').length > 0) {
  184. $display = '<input style="float:right;" id="faf-toggle-comments" class="button" type="button" value="Toggle Filtered Comments (' + $('.hidden-comment').length + ')"></input>';
  185. // Classic
  186. $('table.container-comment').first().parent().parent().prev().children().append($display);
  187. // Beta
  188. $($display).insertAfter('.tags-row');
  189. }
  190. }
  191.  
  192. // Notifications
  193. function filtersNotifications() {
  194. if ($('.hidden-notification').length > 0) {
  195. $display = '<input id="faf-toggle-notifications" class="button" type="button" value="Toggle Filtered Notifications (' + $('.hidden-notification').length + ')"></input>';
  196. $('.global-controls').append($display);
  197. // = Notification Count =
  198. // Classic
  199. if ($('fieldset[id^="messages-watches"] .hidden-notification').length > 0)
  200. $('fieldset[id^="messages-watches"] h3').append(' (' + $('fieldset[id^="messages-watches"] .hidden-notification').length + ' filtered)');
  201. if ($('fieldset[id^="messages-comments-submission"] .hidden-notification').length > 0)
  202. $('fieldset[id^="messages-comments-submission"] h3').append(' (' + $('fieldset[id^="messages-comments-submission"] .hidden-notification').length + ' filtered)');
  203. if ($('fieldset[id^="messages-shouts"] .hidden-notification').length > 0)
  204. $('fieldset[id^="messages-shouts"] h3').append(' (' + $('fieldset[id^="messages-shouts"] .hidden-notification').length + ' filtered)');
  205. if ($('fieldset[id^="messages-favorites"] .hidden-notification').length > 0)
  206. $('fieldset[id^="messages-favorites"] h3').append(' (' + $('fieldset[id^="messages-favorites"] .hidden-notification').length + ' filtered)');
  207. // Beta
  208. if ($('div[id^="messages-watches"] .hidden-notification').length > 0)
  209. $('div[id^="messages-watches"]').prev().find('h3').append(' (' + $('div[id^="messages-watches"] .hidden-notification').length + ' filtered)');
  210. if ($('div[id^="messages-comments-submission"] .hidden-notification').length > 0)
  211. $('div[id^="messages-comments-submission"]').prev().find('h3').append(' (' + $('div[id^="messages-comments-submission"] .hidden-notification').length + ' filtered)');
  212. if ($('div[id^="messages-shouts"] .hidden-notification').length > 0)
  213. $('div[id^="messages-shouts"]').prev().find('h3').append(' (' + $('div[id^="messages-shouts"] .hidden-notification').length + ' filtered)');
  214. if ($('div[id^="messages-favorites"] .hidden-notification').length > 0)
  215. $('div[id^="messages-favorites"]').prev().find('h3').append(' (' + $('div[id^="messages-favorites"] .hidden-notification').length + ' filtered)');
  216. if ($('div[id^="messages-journals"] .hidden-notification').length > 0)
  217. $('div[id^="messages-journals"]').prev().find('h3').append(' (' + $('div[id^="messages-journals"] .hidden-notification').length + ' filtered)');
  218. }
  219. }
  220.  
  221. // Show/Hide Submissions
  222. $(document.body).on('click', '#faf-toggle-subs', function() {
  223. $('.hidden-sub').toggle();
  224. });
  225.  
  226. // Show/Hide Shouts
  227. $(document.body).on('click', '#faf-toggle-shouts', function() {
  228. $('.hidden-shout').toggle();
  229. $('.hidden-shout-br').toggle();
  230. });
  231.  
  232. // Show/Hide Comments
  233. $(document.body).on('click', '#faf-toggle-comments', function() {
  234. $('.hidden-comment').toggle();
  235. })
  236.  
  237. // Show/Hide Notifications
  238. $(document.body).on('click', '#faf-toggle-notifications', function() {
  239. $('.hidden-notification').toggle();
  240. })
  241.  
  242. // == User Settings ==
  243. function displaySettings() {
  244. // Navbar link
  245. $('<li class="noblock"><a target="_blank" href="/controls/site-settings#fa-filter">FA Filter</a></li>').insertAfter($('li.sfw-toggle'));
  246. if (window.location.pathname.lastIndexOf('/controls/site-settings', 0) === 0) {
  247. // Brute forced, but there are no tables in the beta layout site-settings page. This is one of the major differences.
  248. if (!$('table').length) {
  249. // Beta HTML Code
  250. var settingsDisplay = '<div class="container-item-top" style="margin-top: 10px">' +
  251. '<h3 id="fa-filter">FA Filter</h3>' +
  252. 'Hide the things that you dislike!' +
  253. '</div>' +
  254. '<div class="container-item-bot">' +
  255. '<div class="lineitem">' +
  256. '<div class="row">' +
  257. '<div class="cell mobiletoggle">' +
  258. '<strong>Add a User</strong><br/>' +
  259. '<p>Tired of seeing somebody\'s contributions on the site? Add them to your filter list!<br/><strong>Note:</strong> Enter in the username of the person you want to filter, which is the username that would appear after "furaffinity.net/user/".' +
  260. '</div>' +
  261. '<div class="cell mobiletoggle cptoggle">' +
  262. '<input class="textbox" type="text" id="faf-add-username" maxlength="50" style="margin-bottom:10px"></input><br\><input id="faf-add" class="button" type="button" value="Add User" />' +
  263. '</div>' +
  264. '</div>' +
  265. '</div>' +
  266. '</div>' +
  267. '<div class="maintable rounded">' +
  268. '<table class="sessions-list faf-list faf-list-beta" width="100%" cellspacing="0" cellpadding="0" border="0" style="padding:0 15px 10px 15px">' +
  269. '<tbody>' +
  270. '<tr>' +
  271. '<td class="p10t p5r p5b"><h3>Username</h3></td>' +
  272. '<td class="p10t p5r p5b" width="200px"><h3>Submissions</h3></td>' +
  273. '<td class="p10t p5r p5b" width="200px"><h3>Shouts</h3></td>' +
  274. '<td class="p10t p5r p5b" width="200px"><h3>Comments</h3></td>' +
  275. '<td class="p10t p5r p5b" width="200px"><h3>Notifications</h3></td>' +
  276. '</tr>' +
  277. '</tbody>' +
  278. '</table>' +
  279. '</div>' +
  280. '<div class="alignleft p10t">' +
  281. '<input class="button" id="faf-update" type="button" value="Update Filters"> <span class="faf-update-status" style="font-weight: bold; color: #006600; display: none;">Update successful!</span>' +
  282. '</div>';
  283. $(settingsDisplay).insertAfter('.container-item-bot-last');
  284. } else {
  285. // Classic HTML Code
  286. var settingsDisplay = '<table id="fa-filter" cellpadding="0" cellspacing="1" border="0" class="section maintable"><tbody>' +
  287. '<tr><td height="22" class="cat links">&nbsp;<strong>FA Filter</strong></td></tr>' +
  288. '<tr><td class="alt1 addpad ucp-site-settings" align="center">' +
  289. '<table cellspacing="1" cellpadding="0" border="0"><tbody>' +
  290. '<tr>' +
  291. '<th><strong>Add a User</strong></th>' +
  292. '<td><input type="text" id="faf-add-username" maxlength="50"></input>&nbsp;<input id="faf-add" class="button" type="button" value="Add User"></td>' +
  293. '<td class="option-description">' +
  294. '<h3>Hide a user\'s contributions to the site.</h3>' +
  295. '<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>' +
  296. '</td>' +
  297. '</tr>' +
  298. '<tr>' +
  299. '<th class="noborder" style="vertical-align: text-top;"><strong style="position: relative; top: 25px;">Modify Filters</strong></th>' +
  300. '<td class="noborder">' +
  301. '<table cellspacing="0" cellpadding="0" border="0" class="faf-list faf-list-classic">' +
  302. '<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>' +
  303. '</table>' +
  304. '<br><br><input class="button" id="faf-update" type="button" value="Update Filters"> <span class="faf-update-status" style="font-weight: bold; color: #006600; display: none;">Update successful!</span>' +
  305. '</td>' +
  306. '<td class="option-description noborder">' +
  307. '<h3>Choose what items you don\'t want to see.</h3>' +
  308. '<p>If you still want to see some of the things that a user contributes, you can control that here.</p>' +
  309. '</td>' +
  310. '</tr>' +
  311. '</tbody></table>' +
  312. '</td></tr>' +
  313. '</tbody></table>';
  314. $('form').append(settingsDisplay);
  315. }
  316. // Populate list
  317. $.each(userArray, function(username, data) {
  318. addFilterUser(username, data);
  319. });
  320. }
  321. }
  322.  
  323. // Display user in the filter table
  324. function addFilterUser(username, data) {
  325. // Classic
  326. if ($('table.faf-list-classic').length) {
  327. var row = '<tr class="checked" id="filter-' + username + '"><td class="noborder"><a class="fa-filter-remove" id="faf-rm-' + username + '" href="#!">[x]</a> ' + username + '</td>';
  328. 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>'; }
  329. 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>'; }
  330. 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>'; }
  331. 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>'; }
  332. row += '</tr>';
  333. $('table.faf-list tr:last').after(row);
  334. // Beta
  335. } else {
  336. var rowBeta = '<tr id="filter-' + username + '"><td class="p5r" valign="middle" width="auto"><a class="fa-filter-remove" id="faf-rm-' + username + '" href="#!">[x]</a> ' + username + '</td>';
  337. 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>'; }
  338. 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>'; }
  339. 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>'; }
  340. 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>'; }
  341.  
  342. rowBeta += '</tr>';
  343. $('table.faf-list tr:last').after(rowBeta);
  344. }
  345. }
  346.  
  347. // Add
  348. $(document.body).on('click', '#faf-add', function() {
  349. var username = $.trim($('#faf-add-username').val());
  350. $('#faf-add-username').val('');
  351. if (username !== '') {
  352. username = username.toLowerCase();
  353. if (!(username in userArray)) {
  354. userArray[username] = {'subs':1, 'shouts':1, 'coms':1, 'notifications':1};
  355. addFilterUser(username, userArray[username]);
  356. }
  357. }
  358. });
  359.  
  360. // Remove
  361. $(document.body).on('click', 'a.fa-filter-remove', function(event) {
  362. var username = event.target.id.substr(7);
  363. delete userArray[username];
  364. // Replace periods/colons with escaped versions. Who the fuck allows periods in usernames, seriously?
  365. userEsc = username.replace(/\./, '\\.');
  366. userEsc = userEsc.replace(/:/, '\:');
  367. console.log(userEsc)
  368. $('table.faf-list tr#filter-' + userEsc).remove();
  369. });
  370.  
  371. // Update
  372. $(document.body).on('click', '#faf-update', function() {
  373. $('.faf-list tr[id^="filter-"]').each(function() {
  374. var username = this.id.substr(7);
  375. var vals = {'subs':0, 'shouts':0, 'coms':0, 'notifications':0};
  376. // Replace periods/colons with escaped versions. Who the fuck allows periods in usernames, seriously?
  377. userEsc = username.replace(/\./, '\\.');
  378. userEsc = userEsc.replace(/:/, '\:');
  379. // Check checkboxes
  380. if ($('#faf-check-subs-' + userEsc).is(':checked')) { vals['subs'] = 1; }
  381. if ($('#faf-check-shouts-' + userEsc).is(':checked')) { vals['shouts'] = 1; }
  382. if ($('#faf-check-coms-' + userEsc).is(':checked')) { vals['coms'] = 1; }
  383. if ($('#faf-check-notifications-' + userEsc).is(':checked')) { vals['notifications'] = 1; }
  384. userArray[username] = vals;
  385. });
  386. // Save
  387. writeSettings();
  388. // Display message
  389. $('.faf-update-status').fadeIn('slow');
  390. setTimeout(function() {
  391. $('.faf-update-status').fadeOut('slow');
  392. }, 5000);
  393. });
  394.  
  395. displaySettings();
  396.  
  397. setTimeout(parseSettings, 50);
  398. //setTimeout(parseTagSettings, 100);
  399.  
  400. // Submissions
  401. if (window.location.pathname.lastIndexOf('/browse', 0) === 0) setTimeout(filtersSubs, 100);
  402. else if (window.location.pathname.lastIndexOf('/favorites', 0) === 0) setTimeout(filtersSubs, 100);
  403. else if (window.location.pathname.lastIndexOf('/msg/submissions', 0) === 0) setTimeout(filtersSubsFollow, 100);
  404. // Shouts
  405. else if (window.location.pathname.lastIndexOf('/user', 0) === 0) setTimeout(filtersShouts, 100);
  406. else if (window.location.pathname.lastIndexOf('/controls/shouts', 0) === 0) setTimeout(filtersShoutsControl, 100);
  407. // Comments
  408. else if (window.location.pathname.lastIndexOf('/view', 0) === 0) setTimeout(filtersComments, 100);
  409. // Notifications
  410. else if (window.location.pathname.lastIndexOf('/msg/others', 0) === 0) setTimeout(filtersNotifications, 100);