// ==UserScript== // @name ao3 savior // @description Tumblr savior clone for AO3 works pages. // @namespace ao3 // @include http*://archiveofourown.org/* // @grant none // @version 1.5 // @downloadURL none // ==/UserScript== /**** CONFIG ********************/ window.ao3SaviorConfig = { showReasons: true, // set to false if you don't want to see why works were hidden showPlaceholders: true, // set to false if you don't want to see the "This work is hidden!" boxes (could result in empty works lists. Check out the "saved filters" script) authorBlacklist: ['Hated Author 1', 'smeyer'], // excludes works with an author that exactly matches at least one term tagBlacklist: ['dobby', 'jar jar binks', '*mimes'], // excludes works with a tag that matches at least one term. Use * for wildcard tagWhitelist: [], // if a work contains any of these tags, it will not be blocked, even if it matches one of the blacklists summaryBlacklist: ['horse-sized ducks', 'duck-sized horses', "bee's knees"] // excludes works with summaries that contain at least one term }; /********************************/ (function($) { var _works = $('li.blurb'); if (!_works[0]) return; var _toggleClass = 'ao3-savior-hide-toggle'; var _bl = window.ao3SaviorConfig; _works.each(function() { var reason = shouldBlockWork($(this)); if (reason) { blockWork($(this), reason) } }); function blockWork(work, reason) { var cut = $('
').addClass('cut').html(work.html()), reason = '(' +reason +'.)', thisFold = makeFold(), reasonContainer = thisFold.find('.reason'); if (!ao3SaviorConfig.showPlaceholders) { work.hide(); return; } if (ao3SaviorConfig.showReasons) { reasonContainer.html(reason); } else { reasonContainer.remove(); } work.empty().append(thisFold, cut.hide()); work.find('a.action').click(function() { var fold = $(this).closest('.fold'), cut = fold.next('.cut'); cut.add(fold.children('.'+_toggleClass)).toggle(); $(this).text(cut.is(':visible') ? 'Hide' : 'Unhide'); }) function makeFold() { return $('

').addClass('fold').append( $('').addClass(_toggleClass).text('This work is hidden! '), $('').addClass(_toggleClass).html('This work was hidden. ').hide(), $('').addClass('reason'), $('').addClass('actions').append( $('').addClass('action').css({ 'position': 'absolute', 'right': 8, 'top': 10 }).text('Unhide') ) ); } } function shouldBlockWork(work) { var authors = work.find('a[rel=author]'), tags = work.find('a.tag'), summary = work.find('blockquote.summary').text(), reason = 'Reason: '; for (var i = 0, tag; tag = $(tags[i]).text(); i++) { for (var j = 0, wlTag; wlTag = _bl.tagWhitelist[j]; j++) { if (tag == wlTag) { return ''; } } for (var j = 0, blTag; blTag = _bl.tagBlacklist[j]; j++) { if (termsMatch(tag, blTag)) { return reason +'tags include ' +blTag +''; } } } for (var i = 0, blAuthor; blAuthor = _bl.authorBlacklist[i]; i++) { var done; if (blAuthor == 'Anonymous' && !authors[0]) { done = true; } else { for (var j = 0, author; author = $(authors[j]).text(); j++) { if (author == blAuthor) { done = true; break; } } } if (done) { return reason +'author is ' +blAuthor +''; } } for (var i = 0, blSummaryTerm; blSummaryTerm = _bl.summaryBlacklist[i]; i++) { if (summary.indexOf(blSummaryTerm) != -1) { return reason +'summary includes ' +blSummaryTerm +''; } } return ''; function termsMatch(testTerm, listTerm) { testTerm = testTerm.toLowerCase(); listTerm = listTerm.toLowerCase(); if (testTerm == listTerm) { return true; } if (listTerm.indexOf('*') == -1) return false; var parts = listTerm.split('*'), prevPartIndex = 0, firstPart, lastPart; for (var i = 0, part, len = parts.length; i < len; i++) { part = parts[i]; partIndex = testTerm.indexOf(part); if (part && partIndex < prevPartIndex) { return false; } prevPartIndex = partIndex + part.length; } firstPart = parts[0]; lastPart = parts[parts.length-1]; return !( firstPart && testTerm.indexOf(firstPart) != 0 || lastPart && testTerm.indexOf(lastPart)+lastPart.length != testTerm.length ); } } })(window.jQuery);