fb-inline-search.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. (function () {
  2. var BAR_ID = 'fb-inline-search';
  3. function q(sel, root) { return (root || document).querySelector(sel); }
  4. function qa(sel, root) { return Array.from((root || document).querySelectorAll(sel)); }
  5. function ensureBar() {
  6. var bar = q('#' + BAR_ID);
  7. if (bar) return bar;
  8. bar = document.createElement('div');
  9. bar.id = BAR_ID;
  10. bar.innerHTML = '' +
  11. '<input id="fb-inline-search-input" type="text" autocomplete="off" placeholder="筛选当前目录文件(Ctrl+F)" />' +
  12. '<button class="fb-clear" type="button" title="清空">×</button>' +
  13. '<span class="fb-count"></span>';
  14. document.body.appendChild(bar);
  15. var input = q('#fb-inline-search-input', bar);
  16. var clearBtn = q('.fb-clear', bar);
  17. input.addEventListener('input', applyFilter);
  18. input.addEventListener('keydown', function (e) {
  19. if (e.key === 'Escape') {
  20. input.value = '';
  21. applyFilter();
  22. }
  23. });
  24. clearBtn.addEventListener('click', function () {
  25. input.value = '';
  26. applyFilter();
  27. input.focus();
  28. });
  29. return bar;
  30. }
  31. function getItems() {
  32. var nodes = qa('#listing .item, #listing [data-type], #listing tr, #listing li');
  33. var seen = new Set();
  34. return nodes.filter(function (el) {
  35. if (!el || seen.has(el)) return false;
  36. seen.add(el);
  37. return !!(el.querySelector('.name') || el.getAttribute('data-type') || (el.textContent || '').trim());
  38. });
  39. }
  40. function getName(el) {
  41. var n = q('.name', el);
  42. if (n && n.textContent) return n.textContent.trim();
  43. var a = q('a', el);
  44. if (a && a.textContent) return a.textContent.trim();
  45. return (el.textContent || '').trim();
  46. }
  47. function setVisible(el, show) {
  48. if (show) {
  49. if (el.dataset.fbOrigDisplay !== undefined) {
  50. el.style.display = el.dataset.fbOrigDisplay;
  51. delete el.dataset.fbOrigDisplay;
  52. } else {
  53. el.style.removeProperty('display');
  54. }
  55. el.removeAttribute('data-fb-hidden');
  56. return;
  57. }
  58. if (el.dataset.fbOrigDisplay === undefined) {
  59. el.dataset.fbOrigDisplay = el.style.display || '';
  60. }
  61. el.style.display = 'none';
  62. el.setAttribute('data-fb-hidden', '1');
  63. }
  64. function applyFilter() {
  65. var bar = ensureBar();
  66. var input = q('#fb-inline-search-input', bar);
  67. var count = q('.fb-count', bar);
  68. var query = (input.value || '').trim().toLowerCase();
  69. var items = getItems();
  70. var shown = 0;
  71. items.forEach(function (el) {
  72. var ok = !query || getName(el).toLowerCase().indexOf(query) !== -1;
  73. setVisible(el, ok);
  74. if (ok) shown += 1;
  75. });
  76. count.textContent = query ? (shown + '/' + items.length) : '';
  77. }
  78. function focusSearch() {
  79. var input = q('#fb-inline-search-input') || q('#fb-inline-search-input', ensureBar());
  80. if (!input) return;
  81. input.focus();
  82. input.select();
  83. }
  84. function intercept(evt) {
  85. var ctrlF = evt.type === 'keydown' && (evt.ctrlKey || evt.metaKey) && (evt.key === 'f' || evt.key === 'F');
  86. var btn = evt.type === 'click' && evt.target && evt.target.closest && evt.target.closest('.search-button');
  87. if (!ctrlF && !btn) return;
  88. evt.preventDefault();
  89. evt.stopPropagation();
  90. if (evt.stopImmediatePropagation) evt.stopImmediatePropagation();
  91. ensureBar();
  92. focusSearch();
  93. }
  94. function closeNativeSearch() {
  95. var nativeSearch = q('#search');
  96. if (!nativeSearch) return;
  97. nativeSearch.classList.remove('active', 'ongoing');
  98. nativeSearch.style.display = 'none';
  99. document.body.style.overflow = 'auto';
  100. }
  101. function bootstrap() {
  102. ensureBar();
  103. closeNativeSearch();
  104. applyFilter();
  105. }
  106. window.addEventListener('keydown', intercept, true);
  107. window.addEventListener('click', intercept, true);
  108. var mo = new MutationObserver(function () {
  109. closeNativeSearch();
  110. if (q('#fb-inline-search-input') && q('#fb-inline-search-input').value) applyFilter();
  111. });
  112. bootstrap();
  113. mo.observe(document.documentElement, { childList: true, subtree: true });
  114. })();