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