fastsearch.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import * as params from '@params';
  2. let fuse; // holds our search engine
  3. let resList = document.getElementById('searchResults');
  4. let sInput = document.getElementById('searchInput');
  5. let first, last, current_elem = null
  6. let resultsAvailable = false;
  7. // load our search index
  8. window.onload = function () {
  9. let xhr = new XMLHttpRequest();
  10. xhr.onreadystatechange = function () {
  11. if (xhr.readyState === 4) {
  12. if (xhr.status === 200) {
  13. let data = JSON.parse(xhr.responseText);
  14. if (data) {
  15. // fuse.js options; check fuse.js website for details
  16. let options = {
  17. distance: 100,
  18. threshold: 0.4,
  19. ignoreLocation: true,
  20. keys: [
  21. 'title',
  22. 'permalink',
  23. 'summary',
  24. 'content'
  25. ]
  26. };
  27. if (params.fuseOpts) {
  28. options = {
  29. isCaseSensitive: params.fuseOpts.iscasesensitive ?? false,
  30. includeScore: params.fuseOpts.includescore ?? false,
  31. includeMatches: params.fuseOpts.includematches ?? false,
  32. minMatchCharLength: params.fuseOpts.minmatchcharlength ?? 1,
  33. shouldSort: params.fuseOpts.shouldsort ?? true,
  34. findAllMatches: params.fuseOpts.findallmatches ?? false,
  35. keys: params.fuseOpts.keys ?? ['title', 'permalink', 'summary', 'content'],
  36. location: params.fuseOpts.location ?? 0,
  37. threshold: params.fuseOpts.threshold ?? 0.4,
  38. distance: params.fuseOpts.distance ?? 100,
  39. ignoreLocation: params.fuseOpts.ignorelocation ?? true
  40. }
  41. }
  42. fuse = new Fuse(data, options); // build the index from the json file
  43. }
  44. } else {
  45. console.log(xhr.responseText);
  46. }
  47. }
  48. };
  49. xhr.open('GET', "../index.json");
  50. xhr.send();
  51. }
  52. function activeToggle(ae) {
  53. document.querySelectorAll('.focus').forEach(function (element) {
  54. // rm focus class
  55. element.classList.remove("focus")
  56. });
  57. if (ae) {
  58. ae.focus()
  59. document.activeElement = current_elem = ae;
  60. ae.parentElement.classList.add("focus")
  61. } else {
  62. document.activeElement.parentElement.classList.add("focus")
  63. }
  64. }
  65. function reset() {
  66. resultsAvailable = false;
  67. resList.innerHTML = sInput.value = ''; // clear inputbox and searchResults
  68. sInput.focus(); // shift focus to input box
  69. }
  70. // execute search as each character is typed
  71. sInput.onkeyup = function (e) {
  72. // run a search query (for "term") every time a letter is typed
  73. // in the search box
  74. if (fuse) {
  75. let results;
  76. if (params.fuseOpts) {
  77. results = fuse.search(this.value.trim(), {limit: params.fuseOpts.limit}); // the actual query being run using fuse.js along with options
  78. } else {
  79. results = fuse.search(this.value.trim()); // the actual query being run using fuse.js
  80. }
  81. if (results.length !== 0) {
  82. // build our html if result exists
  83. let resultSet = ''; // our results bucket
  84. for (let item in results) {
  85. resultSet += `<li class="post-entry"><header class="entry-header">${results[item].item.title}&nbsp;»</header>` +
  86. `<a href="${results[item].item.permalink}" aria-label="${results[item].item.title}"></a></li>`
  87. }
  88. resList.innerHTML = resultSet;
  89. resultsAvailable = true;
  90. first = resList.firstChild;
  91. last = resList.lastChild;
  92. } else {
  93. resultsAvailable = false;
  94. resList.innerHTML = '';
  95. }
  96. }
  97. }
  98. sInput.addEventListener('search', function (e) {
  99. // clicked on x
  100. if (!this.value) reset()
  101. })
  102. // kb bindings
  103. document.onkeydown = function (e) {
  104. let key = e.key;
  105. let ae = document.activeElement;
  106. let inbox = document.getElementById("searchbox").contains(ae)
  107. if (ae === sInput) {
  108. let elements = document.getElementsByClassName('focus');
  109. while (elements.length > 0) {
  110. elements[0].classList.remove('focus');
  111. }
  112. } else if (current_elem) ae = current_elem;
  113. if (key === "Escape") {
  114. reset()
  115. } else if (!resultsAvailable || !inbox) {
  116. return
  117. } else if (key === "ArrowDown") {
  118. e.preventDefault();
  119. if (ae == sInput) {
  120. // if the currently focused element is the search input, focus the <a> of first <li>
  121. activeToggle(resList.firstChild.lastChild);
  122. } else if (ae.parentElement != last) {
  123. // if the currently focused element's parent is last, do nothing
  124. // otherwise select the next search result
  125. activeToggle(ae.parentElement.nextSibling.lastChild);
  126. }
  127. } else if (key === "ArrowUp") {
  128. e.preventDefault();
  129. if (ae.parentElement == first) {
  130. // if the currently focused element is first item, go to input box
  131. activeToggle(sInput);
  132. } else if (ae != sInput) {
  133. // if the currently focused element is input box, do nothing
  134. // otherwise select the previous search result
  135. activeToggle(ae.parentElement.previousSibling.lastChild);
  136. }
  137. } else if (key === "ArrowRight") {
  138. ae.click(); // click on active link
  139. }
  140. }