// ==UserScript== // @name ao3 savior // @description Hide specified works on AO3 // @namespace ao3 // @include http*://archiveofourown.org/* // @grant none // @version 1.8 // @downloadURL none // ==/UserScript== ;(function() { var STYLE = '\n html body .ao3-savior-hidden {\n display: none;\n }\n \n .ao3-savior-cut {\n display: none;\n }\n \n .ao3-savior-cut::after {\n clear: both;\n content: \'\';\n display: block;\n }\n \n .ao3-savior-reason {\n margin-left: 5px;\n }\n \n .ao3-savior-hide-reasons .ao3-savior-reason {\n display: none;\n }\n \n .ao3-savior-unhide .ao3-savior-cut {\n display: block;\n }\n \n .ao3-savior-fold {\n align-items: center;\n display: flex;\n justify-content: flex-start;\n }\n \n .ao3-savior-unhide .ao3-savior-fold {\n background: #ffc68e;\n }\n \n button.ao3-savior-toggle {\n margin-left: auto;\n }\n'; function addStyle() { var style = document.createElement('style'); style.innerHTML = STYLE; style.className = 'ao3-savior'; document.head.appendChild(style); } var CSS_NAMESPACE = 'ao3-savior'; var getCut = function getCut(work) { var cut = document.createElement('div'); cut.className = CSS_NAMESPACE + '-cut'; cut.innerHTML = work.innerHTML; return cut; }; var getFold = function getFold(reason) { var fold = document.createElement('div'); var note = document.createElement('span'); fold.className = CSS_NAMESPACE + '-fold'; note.className = CSS_NAMESPACE + '-note'; note.innerHTML = 'This work is hidden!'; fold.appendChild(note); fold.append(' '); fold.appendChild(getReasonSpan(reason)); fold.appendChild(getToggleButton()); return fold; }; var getToggleButton = function getToggleButton() { var button = document.createElement('button'); var unhideClassFragment = ' ' + CSS_NAMESPACE + '-unhide'; button.innerHTML = 'Unhide'; button.className = CSS_NAMESPACE + '-toggle'; button.addEventListener('click', function (event) { var work = event.target.closest('.' + CSS_NAMESPACE + '-work'); if (work.className.indexOf(unhideClassFragment) !== -1) { work.className = work.className.replace(unhideClassFragment, ''); work.querySelector('.' + CSS_NAMESPACE + '-note').innerHTML = 'This work is hidden.'; event.target.innerHTML = 'Unhide'; } else { work.className += unhideClassFragment; work.querySelector('.' + CSS_NAMESPACE + '-note').innerHTML = 'This work was hidden.'; event.target.innerHTML = 'Hide'; } }); return button; }; var getReasonSpan = function getReasonSpan(reason) { var span = document.createElement('span'); var tag = reason.tag, author = reason.author, title = reason.title, summary = reason.summary; var text = void 0; if (tag) { text = 'tags include ' + tag + ''; } else if (author) { text = 'authors include ' + author + ''; } else if (title) { text = 'title is ' + title + ''; } else if (summary) { text = 'summary includes ' + summary + ''; } if (text) { span.innerHTML = '(Reason: ' + text + '.)'; } span.className = CSS_NAMESPACE + '-reason'; return span; }; function blockWork(work, reason, config) { if (!reason) return; var showReasons = config.showReasons, showPlaceholders = config.showPlaceholders; if (showPlaceholders) { var fold = getFold(reason); var cut = getCut(work); work.className += ' ' + CSS_NAMESPACE + '-work'; work.innerHTML = ''; work.appendChild(fold); work.appendChild(cut); if (!showReasons) { work.className += ' ' + CSS_NAMESPACE + '-hide-reasons'; } } else { work.className += ' ' + CSS_NAMESPACE + '-hidden'; } } function matchTermsWithWildCard(term, pattern) { if (term.toLowerCase() === pattern.toLowerCase()) return true; if (pattern.indexOf('*') === -1) return false; var regexified = pattern.replace(/\*/g, '.*?'); var regex = new RegExp('^' + regexified + '$', 'i'); return regex.test(term); } var isTagWhitelisted = function isTagWhitelisted(tags, whitelist) { var whitelistLookup = whitelist.reduce(function (lookup, tag) { lookup[tag] = true; return lookup; }, {}); return tags.some(function (tag) { return !!whitelistLookup[tag]; }); }; var findBlacklistedItem = function findBlacklistedItem(list, blacklist, comparator) { var matchingEntry = void 0; list.some(function (item) { blacklist.some(function (entry) { var matched = comparator(item, entry); if (matched) matchingEntry = entry; return matched; }); }); return matchingEntry; }; var equals = function equals(a, b) { return a === b; }; var contains = function contains(a, b) { return a.indexOf(b) !== -1; }; function getBlockReason(_ref, _ref2) { var _ref$authors = _ref.authors, authors = _ref$authors === undefined ? [] : _ref$authors, _ref$title = _ref.title, title = _ref$title === undefined ? '' : _ref$title, _ref$tags = _ref.tags, tags = _ref$tags === undefined ? [] : _ref$tags, _ref$summary = _ref.summary, summary = _ref$summary === undefined ? '' : _ref$summary; var _ref2$authorBlacklist = _ref2.authorBlacklist, authorBlacklist = _ref2$authorBlacklist === undefined ? [] : _ref2$authorBlacklist, _ref2$titleBlacklist = _ref2.titleBlacklist, titleBlacklist = _ref2$titleBlacklist === undefined ? [] : _ref2$titleBlacklist, _ref2$tagBlacklist = _ref2.tagBlacklist, tagBlacklist = _ref2$tagBlacklist === undefined ? [] : _ref2$tagBlacklist, _ref2$tagWhitelist = _ref2.tagWhitelist, tagWhitelist = _ref2$tagWhitelist === undefined ? [] : _ref2$tagWhitelist, _ref2$summaryBlacklis = _ref2.summaryBlacklist, summaryBlacklist = _ref2$summaryBlacklis === undefined ? [] : _ref2$summaryBlacklis; if (isTagWhitelisted(tags, tagWhitelist)) return null; var tag = findBlacklistedItem(tags, tagBlacklist, matchTermsWithWildCard); if (tag) return { tag: tag }; var author = findBlacklistedItem(authors, authorBlacklist, equals); if (author) return { author: author }; if (titleBlacklist.some(function (entry) { return title === entry; })) return { title: title }; var summaryTerm = findBlacklistedItem([summary], summaryBlacklist, contains); if (summaryTerm) return { summary: summaryTerm }; return null; } function isDebug(location) { return location.hostname === 'localhost' || /\ba3sv-debug\b/.test(location.search); } var getText = function getText(element) { return element.textContent.replace(/^\s*|\s*$/g, ''); }; var selectTextsIn = function selectTextsIn(root, selector) { return Array.from(root.querySelectorAll(selector)).map(getText); }; function selectWorkBlockables(workContainerElement) { return { authors: selectTextsIn(workContainerElement, 'a[rel=author]'), tags: selectTextsIn(workContainerElement, 'a.tag'), title: selectTextsIn(workContainerElement, '.header .heading a:first-child')[0], summary: selectTextsIn(workContainerElement, 'blockquote.summary')[0] }; } setTimeout(function () { var debugMode = isDebug(window.location); var config = window.ao3SaviorConfig; var blocked = 0; var total = 0; if (debugMode) { console.groupCollapsed('AO3 SAVIOR'); if (!config) { console.warn('Exiting due to missing config.'); return; } } addStyle(); Array.from(document.querySelectorAll('li.blurb')).forEach(function (work) { var blockables = selectWorkBlockables(work); var reason = getBlockReason(blockables, config); total++; if (reason) { blockWork(work, reason, config); blocked++; if (debugMode) { console.groupCollapsed(work.id + ' blocked'); console.log(work, reason); console.groupEnd(); } } else if (debugMode) { console.groupCollapsed(work.id + ' skipped'); console.log(work); console.groupEnd(); } }); if (debugMode) { console.log('Blocked ' + blocked + ' out of ' + total + ' works'); console.groupEnd(); } }, 10); }()); //# sourceMappingURL=ao3-savior.user.js.map