Greasy Fork 还支持 简体中文。

ao3 saved filters

Adds fields for persistent global & fandom filters to works index pages on AO3

目前為 2020-02-22 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name ao3 saved filters
  3. // @description Adds fields for persistent global & fandom filters to works index pages on AO3
  4. // @namespace ao3
  5. // @include http*://archiveofourown.org/*works*
  6. // @grant none
  7. // @version 1.4
  8. // ==/UserScript==
  9.  
  10. (function($) {
  11.  
  12. // config
  13. var TAG_OWNERSHIP_PERCENT = 70; // the top fandom which owns works in the current tag must own at least this percent in order to be considered the search's active fandom
  14.  
  15. var works = $('#main.works-index'), form = $('form#work-filters');
  16. if (!works[0] || !form[0]) return;
  17.  
  18. var fandomName = (function() {
  19. var fandom = $('#include_fandom_tags label').first().text(),
  20. fandomCount = parseInt(
  21. fandom.substring(fandom.lastIndexOf('(')+1, fandom.lastIndexOf(')'))
  22. ),
  23. tagCount = works.find('.heading').first().text();
  24.  
  25. tagCount = tagCount.substring(0, tagCount.indexOf(' Works'));
  26. tagCount = parseInt(tagCount.substring(tagCount.lastIndexOf(' ')+1));
  27.  
  28. fandom = fandom.substring(0, fandom.lastIndexOf('('));
  29.  
  30. if (!fandom || !fandomCount || !tagCount) { return; }
  31.  
  32. return (fandomCount/tagCount*100 > TAG_OWNERSHIP_PERCENT) ? fandom : null;
  33. })(),
  34. tempKey ='temp-filter', tempFilter = localStorage[tempKey],
  35. tempGlobalKey = 'temp-global-filter', tempGlobalFilter = localStorage[tempGlobalKey],
  36. tempFandomKey = 'temp-fandom-filter', tempFandomFilter = localStorage[tempFandomKey],
  37. globalKey = 'global-filter', fandomKey = fandomName ? 'filter-'+fandomName : '',
  38. globalBox = $('<textarea>').val(localStorage[globalKey] ?
  39. localStorage[globalKey] : ''),
  40. fandomBox = fandomKey ?
  41. globalBox.clone().val(localStorage[fandomKey] ? localStorage[fandomKey] : '') : $(),
  42. search = $('#work_search_query'),
  43. dt = search.parents('dd').first().prev(dt),
  44. realSearch = $('<textarea>')
  45. .attr('name', search.attr('name'))
  46. .css('display', 'none')
  47. .insertAfter(search.removeAttr('name')),
  48. collapser = $('<dt>').addClass('saved-filters-collapser'),
  49. rightArrow = $('<img>').attr('src', '/images/arrow-right.gif?1352358192'),
  50. downArrow = $('<img>').attr('src', '/images/arrow-down.gif?1352358192'),
  51. container = $('<div>').addClass('saved-filters'),
  52. prevSearch = (function() {
  53. var ps, key = realSearch.attr('name')+'=';
  54. if (decodeURIComponent(window.location).indexOf(key) > 0) {
  55. ps = decodeURIComponent(window.location);
  56. ps = ps.substring(ps.indexOf(key)+key.length);
  57. ps = ps.indexOf('&') != -1 ? ps.substring(0, ps.indexOf('&')) : ps;
  58. }
  59. return ps;
  60. })();
  61.  
  62. $('<style>').text(
  63. '.saved-filters-collapser { cursor: pointer; } .saved-filters, saved-filters > div { margin-bottom: 0.6em; } .saved-filters { border-style: solid; border-width: 1px; padding: 0.6em; } .saved-filters textarea { min-height: 8em; } .saved-filters div label { padding-left: 3px; } .prev-search span { color: #000; } .prev-search .temp { background: #ACEA72; } .prev-search .global { background: #93D2ED; } .prev-search .fandom { background: #B9AAED; }'
  64. ).appendTo($('head'));
  65.  
  66. globalBox.addClass('global-filter')
  67. .add(fandomBox.addClass('fandom-filter'))
  68. .each(function() {
  69. var ta = $(this),
  70. cls = ta.attr('class'),
  71. title = cls.charAt(0).toUpperCase() +cls.substring(1, cls.indexOf('-')) +':';
  72.  
  73. $('<div>').addClass(cls)
  74. .prepend(title)
  75. .append(ta.removeClass(),
  76. $('<label>').text('Enabled')
  77. .prepend(
  78. $('<input>').attr({'type': 'checkbox'})
  79. .css({
  80. clip: 'auto',
  81. 'margin-right': '0.3em',
  82. position: 'relative',
  83. width: 'auto',
  84. })
  85. ),
  86. $('<a>').addClass('action').text('Save')
  87. ).appendTo(container);
  88. });
  89.  
  90. container.find('input[type="checkbox"]').each(function() {
  91. var c = $(this), a = c.siblings('a.action'),
  92. key = a.parents('.global-filter')[0] ? globalKey : fandomKey,
  93. checked = localStorage[key+'-on'] !== 'false';
  94.  
  95. c.attr('checked', checked);
  96. a.click(function() {
  97. localStorage[key] = a.siblings('textarea').val();
  98. localStorage[key+'-on'] = c.is(':checked')+'';
  99. });
  100. });
  101.  
  102. if (tempFilter && search.val().indexOf(tempFilter) != -1) {
  103. search.val(tempFilter);
  104. } else {
  105. localStorage[tempFilter] = '';
  106. search.val('');
  107. }
  108.  
  109. container = $('<dd>').append(container);
  110.  
  111. collapser.prepend(rightArrow,
  112. ' Saved filters'
  113. ).click(function() {
  114. container.toggle();
  115. collapser.children('img').replaceWith(
  116. container.is(':visible') ? downArrow : rightArrow);
  117. }).add(container.hide()).insertBefore(dt);
  118.  
  119. form.submit(function() {
  120. var val = search.val() || '', ta, key;
  121. container.find('textarea').each(function() {
  122. ta = $(this),
  123. key = ta.parents('.global-filter')[0] ? tempGlobalKey : tempFandomKey;
  124.  
  125. if (ta.val() && ta.next('input').is(':checked')) {
  126. localStorage[key] = ta.val();
  127. if ((' '+val+' ').indexOf(' '+ta.val()+' ') < 0) {
  128. val += ' '+ta.val();
  129. }
  130. } else if (localStorage[key]) { localStorage[key] = ''; }
  131. });
  132.  
  133. localStorage[tempKey] = search.val();
  134. realSearch.val(val);
  135. });
  136.  
  137. if (prevSearch) {
  138. prevSearch = 'Your filter was: ' +decodeURIComponent(prevSearch).split('+').join(' ') +'.';
  139. if (tempFilter) {
  140. prevSearch = prevSearch.replace(tempFilter, '<span class="temp">'+tempFilter+'</span>');
  141. }
  142. if (tempGlobalFilter) {
  143. prevSearch = prevSearch.replace(tempGlobalFilter, '<span class="global">'+tempGlobalFilter+'</span>');
  144. }
  145. if (tempFandomFilter) {
  146. prevSearch = prevSearch.replace(tempFandomFilter, '<span class="fandom">'+tempFandomFilter+'</span>');
  147. }
  148.  
  149. works.find('.heading').first().after(
  150. $('<div>').addClass('prev-search').append(prevSearch));
  151. } else if ((localStorage[globalKey] && localStorage[globalKey+'-on'] !== 'false') ||
  152. (localStorage[fandomKey] && localStorage[fandomKey+'-on'] !== 'false')) {
  153. form.submit();
  154. }
  155.  
  156. })(window.jQuery);