github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/doc/themes/docsy/assets/js/offline-search.js (about) 1 // Adapted from code by Matt Walters https://www.mattwalters.net/posts/hugo-and-lunr/ 2 3 (function ($) { 4 'use strict'; 5 6 $(document).ready(function () { 7 const $searchInput = $('.td-search-input'); 8 9 // 10 // Options for popover 11 // 12 13 $searchInput.data('html', true); 14 $searchInput.data('placement', 'bottom'); 15 $searchInput.data( 16 'template', 17 '<div class="popover offline-search-result" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>' 18 ); 19 20 // 21 // Register handler 22 // 23 24 $searchInput.on('change', (event) => { 25 render($(event.target)); 26 27 // Hide keyboard on mobile browser 28 $searchInput.blur(); 29 }); 30 31 // Prevent reloading page by enter key on sidebar search. 32 $searchInput.closest('form').on('submit', () => { 33 return false; 34 }); 35 36 // 37 // Lunr 38 // 39 40 let idx = null; // Lunr index 41 const resultDetails = new Map(); // Will hold the data for the search results (titles and summaries) 42 43 // Set up for an Ajax call to request the JSON data file that is created by Hugo's build process 44 $.ajax($searchInput.data('offline-search-index-json-src')).then( 45 (data) => { 46 idx = lunr(function () { 47 this.ref('ref'); 48 49 // If you added more searchable fields to the search index, list them here. 50 // Here you can specify searchable fields to the search index - e.g. individual toxonomies for you project 51 // With "boost" you can add weighting for specific (default weighting without boost: 1) 52 this.field('title', { boost: 5 }); 53 this.field('categories', { boost: 3 }); 54 this.field('tags', { boost: 3 }); 55 // this.field('projects', { boost: 3 }); // example for an individual toxonomy called projects 56 this.field('description', { boost: 2 }); 57 this.field('body'); 58 59 data.forEach((doc) => { 60 this.add(doc); 61 62 resultDetails.set(doc.ref, { 63 title: doc.title, 64 excerpt: doc.excerpt, 65 }); 66 }); 67 }); 68 69 $searchInput.trigger('change'); 70 } 71 ); 72 73 const render = ($targetSearchInput) => { 74 // Dispose the previous result 75 $targetSearchInput.popover('dispose'); 76 77 // 78 // Search 79 // 80 81 if (idx === null) { 82 return; 83 } 84 85 const searchQuery = $targetSearchInput.val(); 86 if (searchQuery === '') { 87 return; 88 } 89 90 const results = idx 91 .query((q) => { 92 const tokens = lunr.tokenizer(searchQuery.toLowerCase()); 93 tokens.forEach((token) => { 94 const queryString = token.toString(); 95 q.term(queryString, { 96 boost: 100, 97 }); 98 q.term(queryString, { 99 wildcard: 100 lunr.Query.wildcard.LEADING | 101 lunr.Query.wildcard.TRAILING, 102 boost: 10, 103 }); 104 q.term(queryString, { 105 editDistance: 2, 106 }); 107 }); 108 }) 109 .slice( 110 0, 111 $targetSearchInput.data('offline-search-max-results') 112 ); 113 114 // 115 // Make result html 116 // 117 118 const $html = $('<div>'); 119 120 $html.append( 121 $('<div>') 122 .css({ 123 display: 'flex', 124 justifyContent: 'space-between', 125 marginBottom: '1em', 126 }) 127 .append( 128 $('<span>') 129 .text('Search results') 130 .css({ fontWeight: 'bold' }) 131 ) 132 .append( 133 $('<i>') 134 .addClass('fas fa-times search-result-close-button') 135 .css({ 136 cursor: 'pointer', 137 }) 138 ) 139 ); 140 141 const $searchResultBody = $('<div>').css({ 142 maxHeight: `calc(100vh - ${ 143 $targetSearchInput.offset().top - 144 $(window).scrollTop() + 145 180 146 }px)`, 147 overflowY: 'auto', 148 }); 149 $html.append($searchResultBody); 150 151 if (results.length === 0) { 152 $searchResultBody.append( 153 $('<p>').text(`No results found for query "${searchQuery}"`) 154 ); 155 } else { 156 results.forEach((r) => { 157 const doc = resultDetails.get(r.ref); 158 const href = 159 $searchInput.data('offline-search-base-href') + 160 r.ref.replace(/^\//, ''); 161 162 const $entry = $('<div>').addClass('mt-4'); 163 164 $entry.append( 165 $('<small>').addClass('d-block text-muted').text(r.ref) 166 ); 167 168 $entry.append( 169 $('<a>') 170 .addClass('d-block') 171 .css({ 172 fontSize: '1.2rem', 173 }) 174 .attr('href', href) 175 .text(doc.title) 176 ); 177 178 $entry.append($('<p>').text(doc.excerpt)); 179 180 $searchResultBody.append($entry); 181 }); 182 } 183 184 $targetSearchInput.on('shown.bs.popover', () => { 185 $('.search-result-close-button').on('click', () => { 186 $targetSearchInput.val(''); 187 $targetSearchInput.trigger('change'); 188 }); 189 }); 190 191 // Enable inline styles in popover. 192 const whiteList = $.fn.tooltip.Constructor.Default.whiteList; 193 whiteList['*'].push('style'); 194 195 $targetSearchInput 196 .data('content', $html[0].outerHTML) 197 .popover({ whiteList: whiteList }) 198 .popover('show'); 199 }; 200 }); 201 })(jQuery);