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 })();