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 // Skip matching when content is included in search and content is empty 66 if (CONFIG.include_content_in_search && data_content === '') { 67 isMatch = false; 68 } else { 69 keywords.forEach(function (keyword, i) { 70 index_title = data_title.indexOf(keyword); 71 index_content = data_content.indexOf(keyword); 72 73 if (index_title < 0 && index_content < 0) { 74 isMatch = false; 75 } else { 76 if (index_content < 0) { 77 index_content = 0; 78 } 79 if (i === 0) { 80 first_occur = index_content; 81 } 82 } 83 }); 84 } 85 // 0x05. show search results 86 if (isMatch) { 87 resultHTML += '<a href=\'' + data_url + '\' class=\'list-group-item list-group-item-action font-weight-bolder search-list-title\'>' + orig_data_title + '</a>'; 88 var content = orig_data_content; 89 if (first_occur >= 0) { 90 // cut out 100 characters 91 var start = first_occur - 20; 92 var end = first_occur + 80; 93 94 if (start < 0) { 95 start = 0; 96 } 97 98 if (start === 0) { 99 end = 100; 100 } 101 102 if (end > content.length) { 103 end = content.length; 104 } 105 106 var match_content = content.substring(start, end); 107 108 // highlight all keywords 109 keywords.forEach(function(keyword) { 110 var regS = new RegExp(keyword, 'gi'); 111 match_content = match_content.replace(regS, '<span class="search-word">' + keyword + '</span>'); 112 }); 113 114 resultHTML += '<p class=\'search-list-content\'>' + match_content + '...</p>'; 115 } 116 } 117 }); 118 if (resultHTML.indexOf('list-group-item') === -1) { 119 return $input.addClass('invalid').removeClass('valid'); 120 } 121 $input.addClass('valid').removeClass('invalid'); 122 $result.html(resultHTML); 123 }); 124 } 125 }); 126 } 127 128 function localSearchReset(searchSelector, resultSelector) { 129 'use strict'; 130 var $input = jQuery(searchSelector); 131 var $result = jQuery(resultSelector); 132 133 if ($input.length === 0) { 134 // eslint-disable-next-line no-console 135 throw Error('No element selected by the searchSelector'); 136 } 137 if ($result.length === 0) { 138 // eslint-disable-next-line no-console 139 throw Error('No element selected by the resultSelector'); 140 } 141 142 $input.val('').removeClass('invalid').removeClass('valid'); 143 $result.html(''); 144 } 145 146 var modal = jQuery('#modalSearch'); 147 var searchSelector = '#local-search-input'; 148 var resultSelector = '#local-search-result'; 149 modal.on('show.bs.modal', function() { 150 var path = CONFIG.search_path || '/local-search.xml'; 151 localSearchFunc(path, searchSelector, resultSelector); 152 }); 153 modal.on('shown.bs.modal', function() { 154 jQuery('#local-search-input').focus(); 155 }); 156 modal.on('hidden.bs.modal', function() { 157 localSearchReset(searchSelector, resultSelector); 158 }); 159})();
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.