Hugo Dark Theme Site Generator https://after-dark.habd.as
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

search.html 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. {{ define "title" -}}
  2. {{ .Title }} | {{ .Site.Title }}
  3. {{- end }}
  4. {{ define "header" }}
  5. {{ partial "menu.html" . }}
  6. {{ end }}
  7. {{ define "main" }}
  8. <header>
  9. <h1>{{ .Title }}</h1>
  10. </header>
  11. <div id="search-app">
  12. <section>
  13. <form v-on:submit.prevent role="search" class="form" action="{{ "search" | absURL }}">
  14. <fieldset class="form-group">
  15. <input v-model="query" id="query" name="s" type="search" class="form-control" maxlength="32" autocomplete="off"{{ with .Params.form.input.placeholder }} placeholder="{{ . }}"{{ end }}{{ if eq .Params.form.input.disabled true }} disabled{{ end }}>
  16. {{ with .Params.form.helpblock }}
  17. <div class="help-block">{{ . | safeHTML }}</div>
  18. {{ end }}
  19. </fieldset>
  20. </form>
  21. </section>
  22. <section v-if="results.length">
  23. <p><i>Showing results for “{ resultsForSearch }”.</i></p>
  24. <div id="search-results">
  25. <article v-for="result in results" itemscope itemtype="http://schema.org/CreativeWork">
  26. <header itemprop="name">
  27. <h2 itemprop="name"><a :href="result.item.url"><span v-html="result.item.title"></span></a></h2>
  28. </header>
  29. <div v-html=result.item.summary itemprop="description"></div>
  30. <nav class="readmore"><p><a itemprop="url" :href="result.item.url">Read More&nbsp;&#x02192;</a></p></nav>
  31. </article>
  32. </div>
  33. </section>
  34. </div>
  35. {{ end }}
  36. {{ define "footer" }}
  37. {{ partial "powered-by.html" . }}
  38. {{ partial "cookie-disclaimer.html" . }}
  39. <script>
  40. fetchInject([
  41. {{ "/js/vue.min.js" | relURL }},
  42. {{ "/js/lodash.custom.min.js" | relURL }},
  43. {{ "/js/fuse.min.js" | relURL }},
  44. {{ "/js/mark.min.js" | relURL }}
  45. ]).then(() => {
  46. (function (window, document, undefined) {
  47. "use strict";
  48. const getQueryByParam = param => decodeURIComponent(
  49. (location.search.split(param + '=')[1] || '').split('&')[0]
  50. ).replace(/\+/g, ' ');
  51. const queryParam = 's';
  52. const selectors = {
  53. appContainer: '#search-app',
  54. resultContainer: '#search-results',
  55. searchInput: '#query'
  56. };
  57. const fuseOpts = {
  58. shouldSort: true,
  59. tokenize: true,
  60. matchAllTokens: true,
  61. includeScore: true,
  62. includeMatches: true,
  63. keys: [
  64. { name: "title", weight: 0.8 },
  65. { name: "contents", weight: 0.5 },
  66. { name: "tags", weight: 0.3 },
  67. { name: "categories", weight: 0.3 }
  68. ]
  69. };
  70. const getSearchInput = () => document.querySelector(selectors.searchInput);
  71. const focusSearchInput = () => getSearchInput().focus();
  72. const searchQuery = getSearchInput().value = getQueryByParam(queryParam);
  73. const fuse = new Fuse([], fuseOpts);
  74. window.fetch('/index.json').then(response => {
  75. response.text().then(searchData => {
  76. fuse.setCollection(JSON.parse(searchData));
  77. searchQuery && search(searchQuery);
  78. });
  79. });
  80. const getUrl = (query) => {
  81. const encodedQuery = encodeURIComponent(query);
  82. const url = {{ .URL }};
  83. return (encodedQuery)
  84. ? `${url}?${queryParam}=${encodedQuery}`
  85. : url;
  86. };
  87. let mark = new Mark(
  88. document.querySelector(
  89. selectors.resultContainer
  90. )
  91. );
  92. const app = new Vue({
  93. delimiters: ['{', '}'],
  94. el: selectors.appContainer,
  95. data: {
  96. fuse: null,
  97. results: [],
  98. query: getQueryByParam(queryParam),
  99. resultsForSearch: getQueryByParam(queryParam)
  100. },
  101. mounted () {
  102. this.fuse = fuse;
  103. window.onpopstate = (evt) => {
  104. this.query = evt.state.query;
  105. };
  106. document.onkeyup = function (evt) {
  107. evt.key === 's' && focusSearchInput();
  108. }
  109. focusSearchInput();
  110. },
  111. watch: {
  112. query () {
  113. this.executeSearch();
  114. window.history.replaceState(
  115. {query: this.query},
  116. null,
  117. getUrl(this.query)
  118. );
  119. }
  120. },
  121. beforeUpdate: function () {
  122. mark.unmark();
  123. },
  124. updated: function () {
  125. this.$nextTick(function () {
  126. mark = new Mark(
  127. document.querySelector(
  128. selectors.resultContainer
  129. )
  130. )
  131. mark.mark(this.query.trim());
  132. })
  133. },
  134. methods: {
  135. executeSearch: _.debounce(function () {
  136. const trimmedQuery = this.query.trim();
  137. this.resultsForSearch = trimmedQuery;
  138. this.results = (trimmedQuery)
  139. ? this.fuse.search(trimmedQuery)
  140. : [];
  141. }, 250)
  142. }
  143. });
  144. const search = query => {
  145. app.results = fuse.search(query);
  146. };
  147. })(window, document);
  148. });
  149. </script>
  150. {{ end }}