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.