/ assets / search.js
search.js
  1  'use strict';
  2  
  3  {{ $searchDataFile := printf "%s.search-data.json" .Language.Lang }}
  4  {{ $searchData := resources.Get "search-data.json" | resources.ExecuteAsTemplate $searchDataFile . | resources.Minify | resources.Fingerprint }}
  5  {{ $searchConfig := i18n "bookSearchConfig" | default "{}" }}
  6  
  7  (function () {
  8    const searchDataURL = '{{ $searchData.RelPermalink }}';
  9    const indexConfig = Object.assign({{ $searchConfig }}, {
 10      includeScore: true,
 11      useExtendedSearch: true,
 12      fieldNormWeight: 1.5,
 13      threshold: 0.2,
 14      ignoreLocation: true,
 15      keys: [
 16        {
 17          name: 'title',
 18          weight: 0.7
 19        },
 20        {
 21          name: 'content',
 22          weight: 0.3
 23        }
 24      ]
 25    });
 26  
 27    const input = document.querySelector('#book-search-input');
 28    const results = document.querySelector('#book-search-results');
 29  
 30    if (!input) {
 31      return
 32    }
 33  
 34    input.addEventListener('focus', init);
 35    input.addEventListener('keyup', search);
 36  
 37    document.addEventListener('keypress', focusSearchFieldOnKeyPress);
 38  
 39    /**
 40     * @param {Event} event
 41     */
 42    function focusSearchFieldOnKeyPress(event) {
 43      if (event.target.value !== undefined) {
 44        return;
 45      }
 46  
 47      if (input === document.activeElement) {
 48        return;
 49      }
 50  
 51      const characterPressed = String.fromCharCode(event.charCode);
 52      if (!isHotkey(characterPressed)) {
 53        return;
 54      }
 55  
 56      input.focus();
 57      event.preventDefault();
 58    }
 59  
 60    /**
 61     * @param {String} character
 62     * @returns {Boolean} 
 63     */
 64    function isHotkey(character) {
 65      const dataHotkeys = input.getAttribute('data-hotkeys') || '';
 66      return dataHotkeys.indexOf(character) >= 0;
 67    }
 68  
 69    function init() {
 70      input.removeEventListener('focus', init); // init once
 71      input.required = true;
 72  
 73      fetch(searchDataURL)
 74        .then(pages => pages.json())
 75        .then(pages => {
 76          window.bookSearchIndex = new Fuse(pages, indexConfig);
 77        })
 78        .then(() => input.required = false)
 79        .then(search);
 80    }
 81  
 82    function search() {
 83      while (results.firstChild) {
 84        results.removeChild(results.firstChild);
 85      }
 86  
 87      if (!input.value) {
 88        return;
 89      }
 90  
 91      const searchHits = window.bookSearchIndex.search(input.value).slice(0,10);
 92      searchHits.forEach(function (page) {
 93        const li = element('<li><a href></a><small></small></li>');
 94        const a = li.querySelector('a'), small = li.querySelector('small');
 95  
 96        a.href = page.item.href;
 97        a.textContent = page.item.title;
 98        small.textContent = page.item.section;
 99  
100        results.appendChild(li);
101      });
102    }
103  
104    /**
105     * @param {String} content
106     * @returns {Node}
107     */
108    function element(content) {
109      const div = document.createElement('div');
110      div.innerHTML = content;
111      return div.firstChild;
112    }
113  })();