PageSourceSearch

https://oslike.github.io/js/local-search.js

js oslike.github.io collected 2026-10-03 09:43:46 UTC 5,751 bytes, 160 lines download raw bytes

1/* global CONFIG */
2
3(function() {
4  // Modified from [hexo-generator-search](https://github.com/wzpan/hexo-generator-search)
5  function localSearchFunc(path, searchSelector, resultSelector) {
6    'use strict';
7    // 0x00. environment initialization
8    var $input = jQuery(searchSelector);
9    var $result = jQuery(resultSelector);
10
11    if ($input.length === 0) {
12      // eslint-disable-next-line no-console
13      throw Error('No element selected by the searchSelector');
14    }
15    if ($result.length === 0) {
16      // eslint-disable-next-line no-console
17      throw Error('No element selected by the resultSelector');
18    }
19
20    if ($result.attr('class').indexOf('list-group-item') === -1) {
21      $result.html('<div class="m-auto text-center"><div class="spinner-border" role="status"><span class="sr-only">Loading...</span></div><br/>Loading...</div>');
22    }
23
24    jQuery.ajax({
25      // 0x01. load xml file
26      url     : path,
27      dataType: 'xml',
28      success : function(xmlResponse) {
29        // 0x02. parse xml file
30        var dataList = jQuery('entry', xmlResponse).map(function() {
31          return {
32            title  : jQuery('title', this).text(),
33            content: jQuery('content', this).text(),
34            url    : jQuery('url', this).text()
35          };
36        }).get();
37
38        if ($result.html().indexOf('list-group-item') === -1) {
39          $result.html('');
40        }
41
42        $input.on('input', function() {
43          // 0x03. parse query to keywords list
44          var content = $input.val();
45          var resultHTML = '';
46          var keywords = content.trim().toLowerCase().split(/[\s-]+/);
47          $result.html('');
48          if (content.trim().length <= 0) {
49            return $input.removeClass('invalid').removeClass('valid');
50          }
51          // 0x04. perform local searching
52          dataList.forEach(function(data) {
53            var isMatch = true;
54            if (!data.title || data.title.trim() === '') {
55              data.title = 'Untitled';
56            }
57            var orig_data_title = data.title.trim();
58            var data_title = orig_data_title.toLowerCase();
59            var orig_data_content = data.content.trim().replace(/<[^>]+>/g, '');
60            var data_content = orig_data_content.toLowerCase();
61            var data_url = data.url;
62            var index_title = -1;
63            var index_content = -1;
64            var first_occur = -1;
65            // only match articles with not empty contents
66            if (data_content !== '') {
67              keywords.forEach(function(keyword, i) {
68                index_title = data_title.indexOf(keyword);
69                index_content = data_content.indexOf(keyword);
70
71                if (index_title < 0 && index_content < 0) {
72                  isMatch = false;
73                } else {
74                  if (index_content < 0) {
75                    index_content = 0;
76                  }
77                  if (i === 0) {
78                    first_occur = index_content;
79                  }
80                  //content_index.push({index_content:index_content, keyword_len:keyword_len});
81                }
82              });
83            } else {
84              isMatch = false;
85            }
86            // 0x05. show search results
87            if (isMatch) {
88              resultHTML += '<a href=\'' + data_url + '\' class=\'list-group-item list-group-item-action font-weight-bolder search-list-title\'>' + orig_data_title + '</a>';
89              var content = orig_data_content;
90              if (first_occur >= 0) {
91                // cut out 100 characters
92                var start = first_occur - 20;
93                var end = first_occur + 80;
94
95                if (start < 0) {
96                  start = 0;
97                }
98
99                if (start === 0) {
100                  end = 100;
101                }
102
103                if (end > content.length) {
104                  end = content.length;
105                }
106
107                var match_content = content.substring(start, end);
108
109                // highlight all keywords
110                keywords.forEach(function(keyword) {
111                  var regS = new RegExp(keyword, 'gi');
112                  match_content = match_content.replace(regS, '<span class="search-word">' + keyword + '</span>');
113                });
114
115                resultHTML += '<p class=\'search-list-content\'>' + match_content + '...</p>';
116              }
117            }
118          });
119          if (resultHTML.indexOf('list-group-item') === -1) {
120            return $input.addClass('invalid').removeClass('valid');
121          }
122          $input.addClass('valid').removeClass('invalid');
123          $result.html(resultHTML);
124        });
125      }
126    });
127  }
128
129  function localSearchReset(searchSelector, resultSelector) {
130    'use strict';
131    var $input = jQuery(searchSelector);
132    var $result = jQuery(resultSelector);
133
134    if ($input.length === 0) {
135      // eslint-disable-next-line no-console
136      throw Error('No element selected by the searchSelector');
137    }
138    if ($result.length === 0) {
139      // eslint-disable-next-line no-console
140      throw Error('No element selected by the resultSelector');
141    }
142
143    $input.val('').removeClass('invalid').removeClass('valid');
144    $result.html('');
145  }
146
147  var modal = jQuery('#modalSearch');
148  var searchSelector = '#local-search-input';
149  var resultSelector = '#local-search-result';
150  modal.on('show.bs.modal', function() {
151    var path = CONFIG.search_path || '/local-search.xml';
152    localSearchFunc(path, searchSelector, resultSelector);
153  });
154  modal.on('shown.bs.modal', function() {
155    jQuery('#local-search-input').focus();
156  });
157  modal.on('hidden.bs.modal', function() {
158    localSearchReset(searchSelector, resultSelector);
159  });
160})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.