1jQuery(document).ready(function () { 2 nSearch.init(); 3}); 4 5var nSearch = function ($) { 6 var inputEle; // There are more than one possible input fields at the same time. 7 8 var lastSearchedWord = ""; // The word that was last requested to the server. 9 var timeoutId = 0; // Used as a handbreak for fast typing. So at max every 345 milliseconds a request will be started. 10 var cleanOutId = 0; // When sending a form, we have to wait for the last suggestion result 11 12 var currentXHR; // Used to abort an asynchronious request when the next request is started. 13 var inputTextArr = []; // Array of words found in the text input field after keyup. 14 var lastListFromServer; // List of words that came last from the server. 15 var filters = {}; 16 var used = false; 17 var actingForm; 18 19 var registerHandlers = function () { 20 // input typing handlers 21 $('body').on('keyup', 'input#edit-keywords', function (e) { 22 if (e.keyCode == 13) { // the "enter" or "return" key 23 return; 24 } else if (e.keyCode == 40 || e.keyCode == 38 ) { // up and down arrow 25 moveSelection(e.keyCode); 26 return; 27 } 28 inputEle = $(this)[0]; 29 inputTextArr = $(this).val().split(' '); 30 let word = inputTextArr[inputTextArr.length - 1]; 31 if (word.length > 2) { 32 // check if this word has the last searched word as prefix - so we don't need another server request. 33 if (lastSearchedWord != "" && word.substr(0, lastSearchedWord.length) == lastSearchedWord) { 34 var sublist = []; 35 for (var i in lastListFromServer) { 36 if (lastListFromServer[i].indexOf(transliterate(word.toLowerCase())) !== -1) { 37 sublist.push(lastListFromServer[i]); 38 } 39 } 40 createList(sublist); 41 } else { 42 clearTimeout(timeoutId); 43 timeoutId = setTimeout(function () { 44 askTheServer(word); 45 }, 345); 46 } 47 } else if (word.length < 3) { 48 $('#search-ajax-list').remove(); 49 } 50 }); 51 // maybe hide suggestion list 52 $('body').on('click', function(e) { 53 let targetId = $(e.target).attr('id'); 54 let parentId = $(e.target).parent().attr('id'); 55 let testArr = ['edit-keywords', 'search-ajax-list']; 56 if (testArr.indexOf(targetId) === -1 && testArr.indexOf(parentId) === -1) { 57 $('#search-ajax-list').remove(); 58 } 59 }); 60 // fill input on click selection 61 $('body').on('click', '#search-ajax-list .item', function () { 62 fillInput($(this).text()); 63 }); 64 // Sometimes the input is filled after page load. 65 // But then there would be no suggestion list for the preset words. 66 $('body').on('focus', 'input#edit-keywords', function (e) { 67 if (!used) { 68 used = true; 69 $(this).val(''); 70 } 71 }); 72 // mark list items as selected 73 $('body').on('mouseenter', '#search-ajax-list .item', function (e) { 74 $('#search-ajax-list .item').removeClass('hover'); 75 $(e.target).addClass('hover'); 76 }); 77 // return key selects and sanatizes before submission 78 $('body').on('keydown', 'input#edit-keywords', function(e) { 79 if (e.keyCode != 13) { 80 return; 81 } 82 // Maybe an item in the list is active. 83 // Then put it in the input field. 84 let hoverItem = $('#search-ajax-list .item.hover'); 85 if (hoverItem.length) { 86 fillInput($(hoverItem).text()); 87 } 88 // sanatize input 89 if (!used) { 90 return; 91 } 92 e.preventDefault(); 93 actingForm = $(this).closest('form')[0]; 94 testForCleanInput(); 95 }); 96 // sanatize before submission 97 $('body').on('click', 'edit-submit-conflux-search', function (e) { 98 if (!used) { 99 return; 100 } 101 e.preventDefault(); 102 actingForm = $(this).closest('form')[0]; 103 testForCleanInput(); 104 }); 105 }; 106 107 // wait for last ajax call to finish before submitting form 108 var testForCleanInput = function () { 109 cleanOutId = setInterval(function () { 110 if (currentXHR == undefined || currentXHR.readyState == 4) { 111 clearInterval(cleanOutId); 112 cleanInput(actingForm); 113 } 114 }, 500); 115 }; 116 117 // sanatize before submission 118 var cleanInput = function(that) { 119 let inputTextArr = $(that).find('input#edit-keywords').val().split(' '); 120 let newInput = ''; // we might alter or remove some words 121 for (var i in inputTextArr) { 122 var inputWord = inputTextArr[i].toLowerCase(); 123 inputWord = transliterate(inputWord); 124 if (inputWord.length < 3) { // to short words are disregarded 125 continue; 126 } 127 if (filters[inputWord] !== undefined) { // found words are kept 128 newInput += inputWord + ' '; 129 } else if (inputWord.length > 3){ 130 let bestSubstitude = bestCleanWord(inputWord); 131 if (bestSubstitude) { 132 newInput += bestSubstitude + ' '; 133 } 134 } 135 } 136 if (newInput != '') { 137 $(that).find('input#edit-keywords').val(newInput); 138 } 139 140 $(that).submit(); 141 }; 142 143 // provides a substitude for a not matching word 144 var bestCleanWord = function(inputWord) { 145 let foundWords = Object.keys(filters); 146 foundWords.sort(function (a, b) { 147 return a.length - b.length || a.localeCompare(b); 148 }); 149 // find shortest superword 150 let shortestSuperword; 151 for (let i in foundWords) { 152 if (foundWords[i].indexOf(inputWord) != -1) { 153 shortestSuperword = foundWords[i]; 154 break; 155 } 156 } 157 let longestSubword; 158 for (let i = foundWords.length - 1; i >= 0; i--) { 159 if (inputWord.indexOf(foundWords[i]) != -1) { 160 longestSubword = foundWords[i]; 161 break; 162 } 163 } 164 if (!shortestSuperword && !longestSubword) { 165 return false;
166 } else if (shortestSuperword && !longestSubword) { 167 return shortestSuperword; 168 } else if (!shortestSuperword && longestSubword) { 169 return longestSubword; 170 } else { 171 let diffSubword = Math.abs(inputWord.length - longestSubword.length); 172 let diffSuperword = Math.abs(inputWord.length - shortestSuperword.length); 173 if (diffSubword == diffSuperword) { 174 return longestSubword; 175 } else if (diffSubword < diffSuperword) { 176 return longestSubword; 177 } else { 178 return shortestSuperword; 179 } 180 } 181 }; 182 183 var askTheServer = function (word) { 184 if (currentXHR != undefined) { 185 currentXHR.abort(); 186 } 187 let catFilters = getCatFilters(); 188 189 word = transliterate(word.toLowerCase()); 190 currentXHR = $.post('/search/autocomplete', { 191 searchtext: word, 192 ids: getFilterIds(), 193 type: catFilters.type, 194 format: catFilters.format 195 } 196 ).done(function (result) { 197 lastListFromServer = result.data; 198 createList(result.data); 199 for (var key in result.ids) { 200 filters[key] = result.ids[key]; 201 } 202 lastSearchedWord = word; 203 }); 204 }; 205 206 var getFilterIds = function() { 207 var preWords = inputTextArr.slice(0, -1); 208 let filterIds = []; 209 for (var i in preWords) { 210 var lowerCase = transliterate(preWords[i].toLowerCase()); 211 if (filters[lowerCase] !== undefined) { 212 filterIds = filterIds.concat(filters[lowerCase]); 213 } 214 } 215 filterIds = filterIds.filter(function(elem, index, self) { 216 return self.indexOf(elem) === index; 217 }); 218 return filterIds; 219 }; 220 221 var getCatFilters = function() { 222 var format = $('select#edit-field-norient-format').val(); 223 if (format == undefined || format == 'All') { 224 format = ''; 225 } 226 var type = $('select#edit-type').val(); 227 if (type == undefined || type == 'All') { 228 type = ''; 229 } 230 231 return { type: type, format: format }; 232 }; 233 234 var createList = function (data) { 235 $('#search-ajax-list').remove(); 236 let theme = 'light'; 237 if ($(inputEle).closest('form').attr('id') == 'views-exposed-form-conflux-search-block-1') { 238 theme = 'dark'; 239 } 240 var html = $('<div id="search-ajax-list" class="' + theme + '"></div>'); 241 let dataCopy = data.slice(0, 5); 242 for (var i in dataCopy) { 243 var inputTextArrCopy = inputTextArr; 244 inputTextArrCopy[inputTextArrCopy.length - 1] = dataCopy[i]; 245 var li = $('<div class="item">' + inputTextArrCopy.join(" ") + '</div>'); 246 $(html).append(li); 247 } 248 let posAndWidth = getPositionAndWidth(); 249 $(html).css({ 250 'position': 'absolute', 251 'top': posAndWidth.top + 'px', 252 'left': posAndWidth.left + 'px', 253 'width': posAndWidth.width 254 }); 255 $('body').append(html); 256 }; 257 258 var fillInput = function (text) { 259 $(inputEle).val(text).focus(); 260 $('#search-ajax-list').remove(); 261 }; 262 263 var getPositionAndWidth = function () { 264 let offset = $(inputEle).offset(); 265 let height = $(inputEle).height(); 266 let width = $(inputEle).width(); 267 return { 268 top: offset.top + height, 269 left: offset.left, 270 height: height, 271 width: width 272 }; 273 }; 274 275 var moveSelection = function(keyCode) { 276 // 40 runter 277 // 38 hoch 278 if (keyCode == 40) { 279 if ($('#search-ajax-list div.hover').length) { 280 let fakeHoverEle = $('#search-ajax-list div.hover'); 281 if ($(fakeHoverEle).next().length) { 282 $(fakeHoverEle).next().addClass('hover'); 283 $(fakeHoverEle).removeClass('hover'); 284 var offsetTop = $(fakeHoverEle).next().position().top + jQuery('#search-ajax-list').scrollTop(); 285 $('#search-ajax-list').scrollTop(offsetTop); 286 } else { 287 $(fakeHoverEle).removeClass('hover'); 288 $('#search-ajax-list div').first().addClass('hover'); 289 $('#search-ajax-list').scrollTop(0); 290 } 291 } else { 292 $('#search-ajax-list div').first().addClass('hover'); 293 } 294 } else if (keyCode == 38) { 295 if ($('#search-ajax-list div.hover').length) { 296 let fakeHoverEle = $('#search-ajax-list div.hover'); 297 if ($(fakeHoverEle).prev().length) { 298 $(fakeHoverEle).prev().addClass('hover'); 299 $(fakeHoverEle).removeClass('hover'); 300 var offsetTop = $(fakeHoverEle).prev().position().top + jQuery('#search-ajax-list').scrollTop(); 301 $('#search-ajax-list').scrollTop(offsetTop); 302 } else { 303 $(fakeHoverEle).removeClass('hover'); 304 $('#search-ajax-list div').last().addClass('hover'); 305 var offsetTop = $('#search-ajax-list div').last().position().top + jQuery('#search-ajax-list').scrollTop(); 306 $('#search-ajax-list').scrollTop(offsetTop); 307 } 308 } else { 309 $('#search-ajax-list div').last().addClass('hover'); 310 } 311 } 312 }; 313 314 var transliterate = function (word) { 315 var answer = ""; 316 var a = {}; 317 a["ä"] = "a"; 318 a["ö"] = "o"; 319 a["ü"] = "u"; 320 a["Ã"] = "ss"; 321 a["Ã¥"] = "a"; 322 a["é"] = "e"; 323 324 for (let i in word) { 325 if (word.hasOwnProperty(i)) { 326 if (a[word[i]] === undefined) { 327 answer += word[i]; 328 } else { 329 answer += a[word[i]]; 330 } 331 } 332 } 333 return answer; 334 }; 335 336 var exports = {}; 337 exports.init = function () { 338// try { 339// var uid = window.drupalSettings.user.uid; 340// var testUids = ["3", "6237", "2526", "2500", "2496", "2492", "2491"]; 341// if (testUids.indexOf(uid) === -1) { 342// return; 343// } 344// } catch (e) { 345// return; 346// } 347 registerHandlers(); 348 }; 349 return exports; 350}(jQuery);
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.