// ==UserScript==
// @name FA Content Filter
// @namespace fa-filter
// @description Filters user-defined content while browsing Furaffinity.
// @include *://www.furaffinity.net/*
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
// @version 1.6.1
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.deleteValue
// @grant GM.openInTab
// @downloadURL none
// ==/UserScript==
// === WARNING ===
// THE TAG FUNCTIONS ARE COMMENTED OUT IN ORDER TO PREVENT ACCIDENTAL DDoS DETECTION ON FURAFFINITY.
this.$ = this.jQuery = jQuery.noConflict(true);
// Shitty workaround, but w/e
async function main() {
// === INITIALIZE USER ARRAY ===
var userArray = JSON.parse(await GM.getValue('userList', '{}'));
var versionNumber = 1.6;
//var tagArray = JSON.parse(GM.getvalue('tagList', '{}'));
// === GENERAL TEMPORARY VARIABLES ===
var filterEnabled = {['subs']:true, ['shouts']:true, ['coms']:true, ['notifications']:true};
// === FILTER ===
var parseSettings = function() {
if (!(userArray instanceof Array)) {
$.each(userArray, function(username, data) {
if (data['subs'] === 1) { hideSubmissions(username); }
if (data['shouts'] === 1) { hideShouts(username); }
if (data['coms'] === 1) { hideComments(username); }
if (data['notifications'] === 1) { hideNotifications(username); }
});
}
};
//var parseTagSettings = function() {
// $('.t-image a[href^="/view"]').each(function() {
// var url = $(this).attr('href');
// console.log(url);
// $.post(url, function(data) {
// console.log($('#keywords', data).text());
// });
// });
//}
// === SAVE ===
function writeSettings() {
GM.setValue('userList', JSON.stringify(userArray));
GM.setValue('versionNumber', versionNumber);
}
// === FUNCTIONS ===
// Hide user submissions
function hideSubmissions(username) {
var submissionBeta = $('figure.u-' + escapeUsername(username));
var submissionFavesBeta = $('figure[data-user="u-' + escapeUsername(username) + '"]');
var submissionInboxBeta = $('a[href="/user/' + username + '"]').closest('figure');
stylizeHidden(submissionBeta);
stylizeHidden(submissionFavesBeta);
stylizeHidden(submissionInboxBeta);
submissionBeta.addClass('hidden-sub').hide();
submissionFavesBeta.addClass('hidden-sub').hide();
submissionInboxBeta.find('input').prop('checked', true);
submissionInboxBeta.addClass('hidden-sub').hide();
if (!filterEnabled['subs']) {
submissionBeta.show();
submissionInboxBeta.show();
}
}
function showSubmissions(username) {
// Browse/Submissions
var submission1 = $('b[id^="sid_"] a[href="/user/' + username + '/"]').closest('b');
var submissionBeta = $('figure.u-' + escapeUsername(username));
var submissionFavesBeta = $('figure[data-user="u-' + escapeUsername(username) + '"]');
var submissionInboxBeta = $('a[href^="/user/' + username + '"]').closest('figure');
undoStylize(submission1);
undoStylize(submissionBeta);
undoStylize(submissionFavesBeta);
undoStylize(submissionInboxBeta);
// Mark Submissions as Checked
submission1.children('small').children('input').prop('checked', false);
submission1.removeClass('hidden-sub').show();
submissionBeta.removeClass('hidden-sub').show();
submissionFavesBeta.removeClass('hidden-sub').show();
submissionInboxBeta.removeClass('hidden-sub').show();
submissionInboxBeta.find('input').prop('checked', false);
// Favorites/Front Page
var submission2 = $('b[id^="sid_"] img[src$="#' + username + '"]').closest('b');
undoStylize(submission2);
submission2.removeClass('hidden-sub').show();
}
// Hide user shouts
function hideShouts(username) {
// Classic
var shout = $('table[id^="shout-"] td.alt1 img[alt="' + username + '"]').closest('table[id^="shout-"]');
shout.addClass('hidden-shout').hide();
stylizeHidden(shout.find('table'));
shout.next('br').addClass('hidden-shout-br').hide();
// Beta
var shoutBeta = $('.comment_container .shout-avatar img[alt="' + username + '"]').closest('.comment_container');
shoutBeta.addClass('hidden-shout').hide();
stylizeHidden(shoutBeta.find('.header'));
stylizeHidden(shoutBeta.find('.body'));
// We want to only highlight and check
var shoutManageBeta = $('table[id^="shout-"] .comments-flex-item-icon img[alt="' + username +'"]').closest('table[id^="shout-"]');
shoutManageBeta.addClass('hidden-shout');
stylizeHidden(shoutManageBeta.find('.comments-userline-flex'));
stylizeHidden(shoutManageBeta.find('.comment_text'));
shoutManageBeta.find('input[type="checkbox"]').prop('checked', true);
}
// Hide user comments and threads
function hideComments(username) {
// Classic
var comments = $('.container-comment td.icon img[alt="' + username + '"]').closest('.container-comment');
$(comments).each(function() {
// Hide comment and get width
if (!($(this).hasClass('hidden-comment'))) {
var width = Number($(this).addClass('hidden-comment').hide().attr('width').slice(0,-1));
var current = $(this).next('.container-comment');
// Iterate through comments until there's a width that is greater than or equal
while (true) {
if (current.length) {
if (Number(current.attr('width').slice(0,-1)) < width) {
current.addClass('hidden-comment').hide();
current = current.next('.container-comment');
} else {
break;
}
} else {
break;
}
}
}
});
// Beta
var commentsBeta = $('.comments-list .comment_container .avatar-desktop img[alt="' + username + '"], .comments-journal .comment_container .avatar-desktop img[alt="' + username + '"]').closest('.comment_container');
stylizeHidden(commentsBeta.find('.header'));
stylizeHidden(commentsBeta.find('.body'));
$(commentsBeta).each(function() {
// Get width, then hide comment
if (!($(this).hasClass('hidden-comment'))) {
var width = $(this).width();
var current = $(this).next('.comment_container');
$(this).addClass('hidden-comment').hide();
// Iterate through the comments until there's a width that is greater than or equal
while (true) {
if (current.length) {
if (current.width() < width) {
current.addClass('hidden-comment').hide();
current = current.next('.comment_container');
} else {
break;
}
} else {
break;
}
}
}
});
}
function showComments(username) {
var comments = $('.comments-list .comment_container .avatar-desktop img[alt="' + username + '"], .comments-journal .comment_container .avatar-desktop img[alt="' + username + '"]').closest('.comment_container');
undoStylize(comments.find('.header'));
undoStylize(comments.find('.body'));
$(comments).each(function() {
if ($(this).hasClass('hidden-comment')) {
var width = $(this).width();
var current = $(this).next('.comment_container');
$(this).removeClass('hidden-comment').show();
// Iterate through the comments until there's a width that is greater than or equal
while (true) {
if (current.length) {
if (current.width() < width) {
current.removeClass('hidden-comment').show();
current = current.next('.comment_container');
} else {
break;
}
} else {
break;
}
}
}
});
}
// Hide user notifications
function hideNotifications(username) {
var notification = $('.message-stream a[href="/user/' + username + '/"]').closest('li');
notification.addClass('hidden-notification').hide();
stylizeHidden(notification);
notification.children('input').prop('checked', true);
// Classic only
notification.children('table').children('tbody').children('tr').children('td').children('.checkbox').children('input').prop('checked', true);
}
function stylizeHidden(item) {
$(item).css({'cssText': 'background-color: #FFBBBB !important'});
$(item).css('color', '#FF0000');
$('a:link', item).css('color', '#FF0000');
$('a:visited', item).css('color', '#FF0000');
}
function undoStylize(item) {
$(item).css('background-color', '');
$(item).css('color', '');
$('a:link', item).css('color', '');
$('a:visited', item).css('color', '');
}
// === UI ===
// == Filter Toggle ==
// Submissions
function filtersSubs() {
// Remove all pre-existing UI for soft-refresh
$('[id="faf-toggle-subs"]').remove();
$('.faf-remove-user-external').parent().remove();
$('.faf-add-user-external').parent().remove();
if ($('.hidden-sub').length > 0) {
if (isBeta()) {
// Beta
$display = '
Toggle Filtered Submissions (' + $('.hidden-sub').length + ') ';
$('.lileft').last().after($display);
} else {
// Classic
// $display = ' ';
// $('form[name="replyform"]').first().append($display);
$display = '⚠ Toggle Filtered Submissions (' + $('.hidden-sub').length + ') ';
$('.search-box-container').first().before($display);
}
} else {
filterEnabled['subs'] = true;
}
$('figure').each(function() {
var username = $(this).attr('class').match('u-([^\\s]+)');
if (!username) {
username = $(this).attr('data-user').match('u-([^\\s]+)');
}
if (username) {
username = username[1];
if (username in userArray && userArray[username]['subs'] === 1) {
$(this).find('figcaption').append('[Unfilter]
');
} else {
$(this).find('figcaption').append('[Filter]
');
}
}
});
}
// Followed Submissions
function filtersSubsFollow() {
if ($('.hidden-sub').length > 0) {
if (isBeta()) {
$display = 'Toggle Filtered Submissions (' + $('.hidden-sub').length + ')
';
$('.actions').css('max-width', '700px');
} else {
$display = ' ';
}
$('.actions').append($display);
}
}
// Shouts
function filtersShouts() {
if ($('.hidden-shout').length > 0) {
$display = ' ';
// Classic
$('table[id^="shout-"]').first().prevAll('table.maintable:first').append($display);
// Beta
$($display).insertBefore($('.section-body .comment_container').first());
}
}
// Shouts (Controls, Beta Only)
function filtersShoutsControl() {
if ($('.hidden-shout').length > 0) {
$display = 'Toggle Filtered Shouts (' + $('.hidden-shout').length + ') ';
$('.section-divider').last().append($display);
$('.hidden-shout input').prop('checked', true);
}
}
// Comments
function filtersComments() {
// Remove all pre-existing UI for soft-refresh
$('[id="faf-toggle-comments"]').remove();
$('.faf-remove-user-external').parent().remove();
$('.faf-add-user-external').parent().remove();
if ($('.hidden-comment').length > 0) {
$display = '';
if (isBeta()) {
// Beta
$($display).insertBefore('#comments-submission, #responsebox');
} else {
// Classic
$('table.container-comment').first().parent().parent().prev().children().append($display);
}
}
$('.comments-list .comment_container, .comments-journal .comment_container').each(function() {
var username = $(this).find('.avatar-desktop img').attr('alt');
if (username) {
if (username in userArray && userArray[username]['coms'] === 1) {
$(this).find('.comment-date').append(' [Unfilter] ');
} else {
$(this).find('.comment-date').append(' [Filter] ');
}
}
});
}
// Notifications
function filtersNotifications() {
if ($('.hidden-notification').length > 0) {
$display = ' ';
$('.global-controls').append($display);
$('.global_controls').append($display);
// = Notification Count =
// Classic
if ($('fieldset[id^="messages-watches"] .hidden-notification').length > 0)
$('fieldset[id^="messages-watches"] h3').append(' (' + $('fieldset[id^="messages-watches"] .hidden-notification').length + ' filtered)');
if ($('fieldset[id^="messages-comments-submission"] .hidden-notification').length > 0)
$('fieldset[id^="messages-comments-submission"] h3').append(' (' + $('fieldset[id^="messages-comments-submission"] .hidden-notification').length + ' filtered)');
if ($('fieldset[id^="messages-shouts"] .hidden-notification').length > 0)
$('fieldset[id^="messages-shouts"] h3').append(' (' + $('fieldset[id^="messages-shouts"] .hidden-notification').length + ' filtered)');
if ($('fieldset[id^="messages-favorites"] .hidden-notification').length > 0)
$('fieldset[id^="messages-favorites"] h3').append(' (' + $('fieldset[id^="messages-favorites"] .hidden-notification').length + ' filtered)');
// Beta
if ($('div[id^="messages-watches"] .hidden-notification').length > 0)
$('div[id^="messages-watches"] h2').append(' (' + $('div[id^="messages-watches"] .hidden-notification').length + ' filtered)');
if ($('div[id^="messages-comments-submission"] .hidden-notification').length > 0)
$('div[id^="messages-comments-submission"] h2').append(' (' + $('div[id^="messages-comments-submission"] .hidden-notification').length + ' filtered)');
if ($('div[id^="messages-shouts"] .hidden-notification').length > 0)
$('div[id^="messages-shouts"] h2').append(' (' + $('div[id^="messages-shouts"] .hidden-notification').length + ' filtered)');
if ($('div[id^="messages-favorites"] .hidden-notification').length > 0)
$('div[id^="messages-favorites"] h2').append(' (' + $('div[id^="messages-favorites"] .hidden-notification').length + ' filtered)');
if ($('div[id^="messages-journals"] .hidden-notification').length > 0)
$('div[id^="messages-journals"] h2').append(' (' + $('div[id^="messages-journals"] .hidden-notification').length + ' filtered)');
}
}
// == Buttons ==
// Show/Hide Submissions
$(document.body).on('click', '#faf-toggle-subs', function() {
$('.hidden-sub').toggle();
filterEnabled['subs'] = !filterEnabled['subs'];
});
// Show/Hide Shouts
$(document.body).on('click', '#faf-toggle-shouts', function() {
$('.hidden-shout').toggle();
$('.hidden-shout-br').toggle();
filterEnabled['shouts'] = !filterEnabled['shouts'];
});
// Show/Hide Comments
$(document.body).on('click', '#faf-toggle-comments', function() {
$('.hidden-comment').toggle();
filterEnabled['coms'] = !filterEnabled['coms'];
});
// Show/Hide Notifications
$(document.body).on('click', '#faf-toggle-notifications', function() {
$('.hidden-notification').toggle();
filterEnabled['notifications'] = !filterEnabled['notifications'];
});
// == External Filters ==
// Add submission filter outside of settings
$(document.body).on('click', '.faf-add-user-external', function() {
var addUser = $(this).attr('id').match('faf-(.*)')[1];
// Add to array
if (!(addUser in userArray)) {
userArray[addUser] = {
'subs': $(this).hasClass('faf-ex-subs') ? 1 : 0,
'shouts': $(this).hasClass('faf-ex-shouts') ? 1 : 0,
'coms': $(this).hasClass('faf-ex-coms') ? 1 : 0,
'notifications': $(this).hasClass('faf-ex-notifications') ? 1 : 0
};
} else {
if ($(this).hasClass('faf-ex-subs')) {
userArray[addUser]['subs'] = 1;
} else if ($(this).hasClass('faf-ex-coms')) {
userArray[addUser]['coms'] = 1;
}
}
// Hide, replace link, and save
if ($(this).hasClass('faf-ex-subs')) {
hideSubmissions(addUser);
filtersSubs();
} else if ($(this).hasClass('faf-ex-coms')) {
hideComments(addUser);
filtersComments();
}
writeSettings();
});
// Remove submission filter outside of settings
$(document.body).on('click', '.faf-remove-user-external', function() {
var removeUser = $(this).attr('id').match('faf-(.*)')[1];
// Remove from array
if (removeUser in userArray) {
if ($(this).hasClass('faf-ex-subs')) {
userArray[removeUser]['subs'] = 0;
} else if ($(this).hasClass('faf-ex-coms')) {
userArray[removeUser]['coms'] = 0;
}
}
// Show, replace link, and save
if ($(this).hasClass('faf-ex-subs')) {
showSubmissions(removeUser);
filtersSubs();
} else if ($(this).hasClass('faf-ex-coms')) {
showComments(removeUser);
filtersComments();
}
writeSettings();
});
// == User Settings ==
function displaySettings() {
// Navbar link
$('FA Filter ').insertAfter($('.navhideonmobile li').last());
// Navbar link (Classic)
$('FA Filter ').insertAfter($('li#sfw-toggle'));
if (window.location.pathname.lastIndexOf('/controls/site-settings', 0) === 0) {
if (isBeta()) {
// Beta HTML Code
var settingsDisplay = '' +
'' +
'
FA Filter ' +
'
' +
'
Add a User ' +
'
' +
'
Tired of seeing somebody\'s contributions on the site? Add them to your filter list! Enter in the username of the person you want to filter, which is the username that would appear after "furaffinity.net/user/".
' +
'
' +
'
' +
' ' +
'
' +
'
' +
'
' +
'
Validate Filters ' +
'
' +
'
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.
' +
'
Validate Only - Simply validates that the usernames that have been entered are correctly formatted.
' +
'
Validate and Remove Unused - Does the same as above and also removes any users with zero filters in the list.
' +
'
' +
'
' +
'' +
'Vaildate Only ' +
'Validate and Remove Unused ' +
' ' +
'Validated! 0 user(s) have been modified or removed. ' +
'
' +
'
' +
'
' +
'
' +
'
' +
'' +
'' +
'Username ' +
'Submissions ' +
'Shouts ' +
'Comments ' +
'Notifications ' +
' ' +
' ' +
'
' +
'
' +
'
' +
'Update successful! ' +
'
' +
'
' +
' ';
$(settingsDisplay).insertBefore($('section').last());
} else {
// Classic HTML Code
var settingsDisplay = '' +
' FA Filter ' +
'' +
'' +
' ' +
'
';
$('form').last().append(settingsDisplay);
}
// Populate list
$.each(userArray, function(username, data) {
addFilterUser(username, data);
});
}
}
// Display user in the filter table
function addFilterUser(username, data) {
if (isBeta()) {
// Beta
var rowBeta = '[x] ' + username + ' ';
if (data['subs'] === 1) { rowBeta += ' '; } else { rowBeta += ' '; }
if (data['shouts'] === 1) { rowBeta += ' '; } else { rowBeta += ' '; }
if (data['coms'] === 1) { rowBeta += ' '; } else { rowBeta += ' '; }
if (data['notifications'] === 1) { rowBeta += ' '; } else { rowBeta += ' '; }
rowBeta += ' ';
$('table.faf-list tr:last').after(rowBeta);
} else {
// Classic
var row = '[x] ' + username + ' ';
if (data['subs'] === 1) { row += ' '; } else { row += ' '; }
if (data['shouts'] === 1) { row += ' '; } else { row += ' '; }
if (data['coms'] === 1) { row += ' '; } else { row += ' '; }
if (data['notifications'] === 1) { row += ' '; } else { row += ' '; }
row += ' ';
$('table.faf-list tr:last').after(row);
}
}
// Add
$(document.body).on('click', '#faf-add', function() {
var username = $.trim($('#faf-add-username').val());
$('#faf-add-username').val('');
if (username !== '') {
username = username.toLowerCase();
username = username.replace(/[_]/g, '');
if (!(username in userArray)) {
userArray[username] = {'subs':1, 'shouts':1, 'coms':1, 'notifications':1};
addFilterUser(username, userArray[username]);
}
}
});
// Remove
$(document.body).on('click', 'a.faf-remove', function(event) {
var username = event.target.id.substr(7);
delete userArray[username];
userEsc = escapeUsername(username);
$('table.faf-list tr#filter-' + userEsc).remove();
});
// Update
$(document.body).on('click', '#faf-update', function() {
$('.faf-list tr[id^="filter-"]').each(function() {
var username = this.id.substr(7);
var vals = {'subs':0, 'shouts':0, 'coms':0, 'notifications':0};
userEsc = escapeUsername(username);
// Check checkboxes
if ($('#faf-check-subs-' + userEsc).is(':checked')) { vals['subs'] = 1; }
if ($('#faf-check-shouts-' + userEsc).is(':checked')) { vals['shouts'] = 1; }
if ($('#faf-check-coms-' + userEsc).is(':checked')) { vals['coms'] = 1; }
if ($('#faf-check-notifications-' + userEsc).is(':checked')) { vals['notifications'] = 1; }
userArray[username] = vals;
});
// Save
writeSettings();
// Display message
$('.faf-update-status').fadeIn('slow');
setTimeout(function() {
$('.faf-update-status').fadeOut('slow');
}, 5000);
});
// Validate
$(document.body).on('click', '#faf-validate', function() {
var modCount = 0;
// Validate
$.each(userArray, function(username, data) {
var tempUsername = username;
tempUsername = tempUsername.trim();
if (tempUsername !== '') {
tempUsername = tempUsername.toLowerCase();
tempUsername = tempUsername.replace(/[_ ]/g, '');
if (tempUsername !== username) {
userArray[tempUsername] = data;
delete userArray[username];
$('tr[id="filter-' + username + '"]').remove();
modCount++;
}
}
});
// Remove empty
if ($('#select-faf-validate-options').val() === 'vr') {
$.each(userArray, function(username, data) {
var isEmpty = true;
$.each(data, function(entity, value) {
if (value === 1) {
isEmpty = false;
}
});
if (isEmpty) {
delete userArray[username];
$('tr[id="filter-' + username + '"]').remove();
modCount++;
}
});
}
// Save
writeSettings();
// Display message
$('.faf-validate-status').text('Validated! ' + modCount + ' user(s) have been modified or removed.');
$('.faf-validate-status').fadeIn('slow');
setTimeout(function() {
$('.faf-validate-status').fadeOut('slow');
}, 5000);
});
// IMPORT/EXPORT
// Clear
$(document.body).on('click', '#faf-port-clear', function() {
$('#faf-raw-port').val('');
});
// Export
$(document.body).on('click', '#faf-export', function() {
var exportVal = {'versionNumber': versionNumber, 'userList': userArray};
$('#faf-raw-port').val(JSON.stringify(exportVal));
});
// Import
$(document.body).on('click', '#faf-import', async function() {
if ($.trim($('#faf-raw-port').val()).length > 0) {
var importJson = null;
try {
importJson = JSON.parse($('#faf-raw-port').val());
await validateAndImportData(importJson);
writeSettings();
$('.faf-import-status').css('color', '#006600').text('Import successful!');
$('.faf-import-status').fadeIn('slow');
setTimeout(function() {
$('.faf-import-status').fadeOut('slow');
}, 5000);
} catch (e) {
$('.faf-import-status').css('color', '#FF6666').text('Invalid data!');
$('.faf-import-status').fadeIn('slow');
setTimeout(function() {
$('.faf-import-status').fadeOut('slow');
}, 5000);
return;
}
}
});
// === UTILITIES ===
function escapeUsername(username) {
// Replace periods/colons/tildes with escaped versions. Who the fuck allows periods AND tildes in usernames, seriously?
userEsc = username.replace(/\./g, '\\.');
userEsc = userEsc.replace(/:/g, '\\:');
userEsc = userEsc.replace(/~/g, '\\~');
return userEsc;
}
function updateCSS() {
// Fuck Greasemonkey
var newCSS = '';
$('head').append(newCSS);
}
function isBeta() {
return $('body').attr('data-static-path') === '/themes/beta';
}
// IMPORT FUNCTIONALITY - UPDATE WITH EACH MAJOR UPDATE
async function validateAndImportData(jsonData) {
switch (jsonData.version) {
case '1.6':
default:
// Version 1.6 - User data only
$.each(jsonData.userList, function(user, filters) {
// Validate each user and filter
$.each(filters, function(type, value) {
if (value != 0 && value != 1) {
throw "Invalid value.";
}
});
userArray[user] = {
'subs': filters.subs,
'shouts': filters.shouts,
'coms': filters.coms,
'notifications': filters.notifications
}
});
}
}
displaySettings();
updateCSS();
setTimeout(parseSettings, 50);
//setTimeout(parseTagSettings, 100);
// Submissions
if (window.location.pathname.lastIndexOf('/browse', 0) === 0) setTimeout(filtersSubs, 100);
else if (window.location.pathname.lastIndexOf('/favorites', 0) === 0) setTimeout(filtersSubs, 100);
else if (window.location.pathname.lastIndexOf('/msg/submissions', 0) === 0) setTimeout(filtersSubsFollow, 100);
// Shouts
else if (window.location.pathname.lastIndexOf('/user', 0) === 0) setTimeout(filtersShouts, 100);
else if (window.location.pathname.lastIndexOf('/controls/shouts', 0) === 0) setTimeout(filtersShoutsControl, 100);
// Comments
else if (window.location.pathname.lastIndexOf('/view', 0) === 0) setTimeout(filtersComments, 100);
else if (window.location.pathname.lastIndexOf('/journal', 0) === 0) setTimeout(filtersComments, 100);
// Notifications
else if (window.location.pathname.lastIndexOf('/msg/others', 0) === 0) setTimeout(filtersNotifications, 100);
else setTimeout(filtersSubs, 100);
}
main();