Greasy Fork

来自缓存

Greasy Fork is available in English.

ao3 savior

Hide specified works on AO3

当前为 2017-11-10 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name          ao3 savior
// @description   Hide specified works on AO3
// @namespace     ao3
// @include       http*://archiveofourown.org/*
// @grant         none
// @version       1.9
// ==/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 <strong>' + tag + '</strong>';
  } else if (author) {
    text = 'authors include <strong>' + author + '</strong>';
  } else if (title) {
    text = 'title is <strong>' + title + '</strong>';
  } else if (summary) {
    text = 'summary includes <strong>' + summary + '</strong>';
  }

  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: [].concat(selectTextsIn(workContainerElement, 'a.tag'), selectTextsIn(workContainerElement, '.required-tags .text')),
    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('- blocked ' + work.id);
        console.log(work, reason);
        console.groupEnd();
      }
    } else if (debugMode) {
      console.groupCollapsed('  skipped ' + work.id);
      console.log(work);
      console.groupEnd();
    }
  });

  if (debugMode) {
    console.log('Blocked ' + blocked + ' out of ' + total + ' works');
    console.groupEnd();
  }
}, 10);
}());

//# sourceMappingURL=ao3-savior.user.js.map