1jQuery.autocomplete = function(input, options) { 2 // Create a link to self 3 var me = this; 4 var currentRequest = null; 5 // Create jQuery object for input element 6 var $input = $(input).attr("autocomplete", "off"); 7 8 // Apply inputClass if necessary 9 if (options.inputClass) $input.addClass(options.inputClass); 10 11 // Create results 12 var results = document.createElement("div"); 13 14 // Create jQuery object for results 15 var $results = $(results); 16 $results.hide().addClass(options.resultsClass).css("position", "absolute"); 17 if( options.width > 0 ) $results.css("width", options.width); 18 19 // Add to body element 20 // input.after(results); 21 $("body").append(results); 22 23 input.autocompleter = me; 24 25 var timeout = null; 26 var prev = ""; 27 var active = -1; 28 var cache = {}; 29 var keyb = false; 30 var hasFocus = false; 31 var lastKeyPressCode = null; 32 33 // flush cache 34 function flushCache(){ 35 cache = {}; 36 cache.data = {}; 37 cache.length = 0; 38 }; 39 40 // flush cache 41 flushCache(); 42 43 // if there is a data array supplied 44 if( options.data != null ){ 45 var sFirstChar = "", stMatchSets = {}, row = []; 46 47 // no url was specified, we need to adjust the cache length to make sure it fits the local data store 48 if( typeof options.url != "string" ) options.cacheLength = 1; 49 50 // loop through the array and create a lookup structure 51 for( var i=0; i < options.data.length; i++ ){ 52 // if row is a string, make an array otherwise just reference the array 53 row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]); 54 55 // if the length is zero, don't add to list 56 if( row[0].length > 0 ){ 57 // get the first character 58 sFirstChar = row[0].substring(0, 1).toLowerCase(); 59 // if no lookup array for this character exists, look it up now 60 if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = []; 61 // if the match is a string 62 stMatchSets[sFirstChar].push(row); 63 } 64 } 65 66 // add the data items to the cache 67 for( var k in stMatchSets ){ 68 // increase the cache size 69 options.cacheLength++; 70 // add to the cache 71 addToCache(k, stMatchSets[k]); 72 } 73 } 74 75 $input 76 .keydown(function(e) { 77 // track last key pressed 78 lastKeyPressCode = e.keyCode; 79 switch(e.keyCode) { 80 case 38: // up 81 e.preventDefault(); 82 moveSelect(-1); 83 break; 84 case 40: // down 85 e.preventDefault(); 86 moveSelect(1); 87 break; 88 case 9: // tab 89 case 13: // return 90 91 break; 92 default: 93 active = -1; 94 if (timeout) clearTimeout(timeout); 95 timeout = setTimeout(function () { 96 onChange(); 97 }, options.delay); 98 break; 99 } 100 }) 101 .focus(function(){ 102 // track whether the field has focus, we shouldn't process any results if the field no longer has focus 103 hasFocus = true; 104 }) 105 .blur(function() { 106 // track whether the field has focus 107 hasFocus = false; 108 hideResults(); 109 }); 110 111 hideResultsNow(); 112 113 function onChange() { 114 // ignore if the following keys are pressed: [del] [shift] [capslock] 115 if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide(); 116 var v = $input.val(); 117 if (v == prev) return; 118 prev = v; 119 if (v.length >= options.minChars) { 120 $input.addClass(options.loadingClass); 121 requestData(v); 122 } else { 123 $input.removeClass(options.loadingClass); 124 $results.hide(); 125 } 126 }; 127 128 function moveSelect(step) { 129 130 var lis = $("li", results); 131 if (!lis) return; 132 133 active += step; 134 135 if (active < 0) { 136 active = 0; 137 } else if (active >= lis.length) { 138 active = lis.length - 1; 139 } 140 141 lis.removeClass("ac_over"); 142 143 $(lis[active]).addClass("ac_over"); 144 145 // Weird behaviour in IE 146 // if (lis[active] && lis[active].scrollIntoView) { 147 // lis[active].scrollIntoView(false); 148 // } 149 150 }; 151 152 function selectCurrent() { 153 var li = $("li.ac_over", results)[0]; 154 if (!li) { 155 var $li = $("li", results); 156 if (options.selectOnly) { 157 if ($li.length == 1) li = $li[0]; 158 } else if (options.selectFirst) { 159 li = $li[0]; 160 } 161 } 162 if (li) { 163 selectItem(li); 164 return true; 165 } else { 166 return false; 167 } 168 }; 169 170 function selectItem(li) { 171 if (!li) { 172 li = document.createElement("li"); 173 li.extra = []; 174 li.selectValue = ""; 175 } 176 var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML); 177 input.lastSelected = v; 178 prev = v; 179 $results.html(""); 180 $input.val(v); 181 hideResultsNow(); 182 if (options.onItemSelect) setTimeout(function() { options.onItemSelect(li) }, 1); 183 }; 184 185 // selects a portion of the input string 186 function createSelection(start, end){ 187 // get a reference to the input element 188 var field = $input.get(0); 189 if( field.createTextRange ){ 190 var selRange = field.createTextRange(); 191 selRange.collapse(true); 192 selRange.moveStart("character", start); 193 selRange.moveEnd("character", end); 194 selRange.select(); 195 } else if( field.setSelectionRange ){ 196 field.setSelectionRange(start, end); 197 } else { 198 if( field.selectionStart ){ 199 field.selectionStart = start; 200 field.selectionEnd = end; 201 } 202 } 203 field.focus(); 204 }; 205 206 // fills in the input box w/the first match (assumed to be the best match) 207 function autoFill(sValue){ 208 // if the last user key pressed was backspace, don't autofill 209 if( lastKeyPressCode != 8 ){ 210 // fill in the value (keep the case the user has typed) 211 $input.val($input.val() + sValue.substring(prev.length)); 212 // select the portion of the value not typed by the user (so the next character will erase) 213 createSelection(prev.length, sValue.length); 214 } 215 }; 216 217 function showResults() { 218 // get the position of the input field right now (in case the DOM is shifted) 219 var pos = findPos(input); 220 // either use the specified width, or autocalculate based on form element 221 var iWidth = (options.width > 0) ? options.width : $input.width(); 222 // do reposition on scroll if body has fixed header 223 224 if ($('body').hasClass('fixed-header')) { 225 $results.css({ 226 width: parseInt(iWidth) + "px", 227 top: (pos.y + input.offsetHeight + 5) + "px", 228 left: pos.x + 3 + "px" 229 }).show(); 230 $results.css({ 231 top: ($(window).scrollTop() + 48) + "px" 232 }); 233 $(window).on("scroll", function() { 234 $results.css({ 235 top: ($(window).scrollTop() + 48) + "px" 236 }); 237 }); 238 } else { 239 if (IS_MOBILE != '1') { 240 $results.css({ 241 width: parseInt(iWidth) + "px", 242 top: (pos.y + input.offsetHeight - 1) + "px", 243 left: pos.x + 3 + "px" 244 }).show(); 245 } else { 246 $results.css({ 247 top: ($(window).scrollTop() + 48) + "px" 248 }).show(); 249 } 250 } 251 }; 252 253 function hideResults() { 254 if (timeout) clearTimeout(timeout); 255 timeout = setTimeout(hideResultsNow, 200); 256 }; 257 258 function hideResultsNow() { 259 if (timeout) clearTimeout(timeout); 260 $input.removeClass(options.loadingClass); 261 if ($results.is(":visible")) { 262 $results.hide(); 263 } 264 if (options.mustMatch) { 265 var v = $input.val(); 266 if (v != input.lastSelected) { 267 selectItem(null); 268 } 269 } 270 }; 271 272 function receiveData(q, data,error=false) { 273 if (data||error) { 274 $input.removeClass(options.loadingClass); 275 results.innerHTML = ""; 276 277 if(error){ 278 let newDiv = document.createElement('div'); 279 newDiv.className = 'my-class'; 280 newDiv.id = 'my-id'; 281 newDiv.innerHTML = '<span style="height: 100%; font-size: 1.5em;">'+data+'</span>'; 282 results.appendChild(newDiv); 283 showResults(); 284 return ; 285 } 286 // if the field no longer has focus or if there are no matches, do not display the drop down 287 288 if( !hasFocus || data.length == 0 ) return hideResultsNow(); 289 290 // if ($.browser.msie) { 291 // // we put a styled iframe behind the calendar so HTML SELECT elements don't show through 292 // $results.append(document.createElement('iframe')); 293 // } 294 results.appendChild(dataToDom(data)); 295 // autofill in the complete box w/the first match as long as the user hasn't entered in more data 296 if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]); 297 var searchid = IS_MOBILE != '1' ? 'searchpr' : 'searchpr1'; 298 var newDiv = document.createElement('div'); 299 newDiv.className = 'my-class'; 300 newDiv.id = 'my-id'; 301 newDiv.innerHTML = '<a class="btn btn-default gradient" id = "show_results" href="javascript:;" onClick="{ var inp = document.getElementById(\''+searchid+'\'); this.href=SEARCH_LANG+\'?keywords=\'+inp.value; }">'+SHOW_RESULTS+' (' + data.length + ') </a>'; 302 results.appendChild(newDiv); 303 showResults(); 304 } else { 305 hideResultsNow(); 306 } 307 }; 308 309 function parseData(data) { 310 if (!data) return null; 311 var parsed = []; 312 var rows = data.split(options.lineSeparator); 313 for (var i=0; i < rows.length; i++) { 314 var row = $.trim(rows[i]); 315 if (row) { 316 parsed[parsed.length] = row.split(options.cellSeparator); 317 } 318 } 319 return parsed; 320 }; 321 322 function dataToDom(data) { 323 var ul = document.createElement("ul"); 324 var num = data.length; 325 326 // limited results to a max number 327 if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow; 328 329 for (var i=0; i < num; i++) { 330 var row = data[i]; 331 if (!row) continue; 332 var li = document.createElement("li"); 333 if (options.formatItem) { 334 li.innerHTML = options.formatItem(row, i, num); 335 li.selectValue = row[0]; 336 } else { 337 li.innerHTML = row[0]; 338 li.selectValue = row[0]; 339 } 340 var extra = null; 341 if (row.length > 1) { 342 extra = []; 343 for (var j=1; j < row.length; j++) { 344 extra[extra.length] = row[j]; 345 } 346 } 347 li.extra = extra; 348 ul.appendChild(li); 349 $(li).hover( 350 function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); }, 351 function() { $(this).removeClass("ac_over"); } 352 ).click(function(e) { e.preventDefault(); e.stopPropagation(); selectItem(this) }); 353 } 354 return ul; 355 }; 356 357 function requestData(q) { 358 if (!options.matchCase) q = q.toLowerCase(); 359 var data = options.cacheLength ? loadFromCache(q) : null; 360 // recieve the cached data 361 if (data != null && data.length) { 362 receiveData(q, data); 363 // if an AJAX url has been supplied, try loading the data now 364 } else if( (typeof options.url == "string") && (options.url.length > 0) ){ 365 currentRequest = jQuery.ajax({ 366 type: 'get', 367 url: makeUrl(q), 368 beforeSend : function() { if(currentRequest != null) currentRequest.abort()}, 369 success: function(data) { 370 let json = JSON.parse(data); 371 data = json.body.join(''); 372 let count = json.count; 373 data = parseData(data); 374 addToCache(q, data); 375 receiveData(q, data); 376 if(json.not_found) 377 receiveData(q,json.not_found,true); 378 setButtonText(SHOW_RESULTS,count); 379 } 380 }); 381 // if there's been no data found, remove the loading class 382 } else { 383 $input.removeClass(options.loadingClass); 384 } 385 }; 386 387 function setButtonText(SHOW_RESULTS,count){ 388 $("#show_results").text(SHOW_RESULTS + " ("+count+")"); 389 } 390 function makeUrl(q) { 391 var url = options.url + "?q=" + encodeURI(q); 392 for (var i in options.extraParams) { 393 url += "&" + i + "=" + encodeURI(options.extraParams[i]); 394 } 395 return url; 396 }; 397 398 function loadFromCache(q) { 399 if (!q) return null; 400 //if (cache.data[q]) return cache.data[q]; 401 if (options.matchSubset) { 402 // for (var i = q.length - 1; i >= options.minChars; i--) { 403 // var qs = q.substr(0, i); 404 // var c = cache.data[qs]; 405 // if (c) { 406 // var csub = []; 407 // for (var j = 0; j < c.length; j++) { 408 // var x = c[j]; 409 // var x0 = x[0]; 410 // if (matchSubset(x0, q)) { 411 // csub[csub.length] = x; 412 // } 413 // } 414 // return csub; 415 // } 416 // } 417 var arySplitRequest = q.split(" "); 418 var availableTags = cache.data; 419 var csub = []; 420 if (Object.keys(availableTags).length) { 421 for (var prop in availableTags) { 422 for (var i = 0; i < availableTags[prop].length; i++) { 423 424 425 var intCount = 0; 426 for (var j = 0; j < arySplitRequest.length; j++) { 427 428 var regexp = new RegExp(arySplitRequest[j], 'i'); 429 430 var test = availableTags[prop][i][0].match(regexp); 431 432 if (test) { 433 434 intCount++; 435 436 } else if (!test) { 437 438 intCount = arySplitRequest.length + 1; 439 440 } 441 442 if (intCount == arySplitRequest.length) { 443 444 csub.push(availableTags[prop][i]); 445 446 } 447 } 448 449 } 450 if (csub.length) break; 451 } 452 return csub; 453 454 } 455 } 456 return null; 457 }; 458 459 function matchSubset(s, sub) { 460 if (!options.matchCase) s = s.toLowerCase(); 461 var i = s.indexOf(sub); 462 if (i == -1) return false;
463 return i == 0 || options.matchContains; 464 }; 465 466 this.flushCache = function() { 467 flushCache(); 468 }; 469 470 this.setExtraParams = function(p) { 471 options.extraParams = p; 472 }; 473 474 this.findValue = function(){ 475 var q = $input.val(); 476 477 if (!options.matchCase) q = q.toLowerCase(); 478 var data = options.cacheLength ? loadFromCache(q) : null; 479 if (data) { 480 findValueCallback(q, data); 481 } else if( (typeof options.url == "string") && (options.url.length > 0) ){ 482 $.get(makeUrl(q), function(data) { 483 data = parseData(data) 484 addToCache(q, data); 485 findValueCallback(q, data); 486 }); 487 } else { 488 // no matches 489 findValueCallback(q, null); 490 } 491 } 492 493 function findValueCallback(q, data){ 494 if (data) $input.removeClass(options.loadingClass); 495 496 var num = (data) ? data.length : 0; 497 var li = null; 498 499 for (var i=0; i < num; i++) { 500 var row = data[i]; 501 502 if( row[0].toLowerCase() == q.toLowerCase() ){ 503 li = document.createElement("li"); 504 if (options.formatItem) { 505 li.innerHTML = options.formatItem(row, i, num); 506 li.selectValue = row[0]; 507 } else { 508 li.innerHTML = row[0]; 509 li.selectValue = row[0]; 510 } 511 var extra = null; 512 if( row.length > 1 ){ 513 extra = []; 514 for (var j=1; j < row.length; j++) { 515 extra[extra.length] = row[j]; 516 } 517 } 518 li.extra = extra; 519 } 520 } 521 522 if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1); 523 } 524 525 function addToCache(q, data) { 526 if (!data || !q || !options.cacheLength) return; 527 if (!cache.length || cache.length > options.cacheLength) { 528 flushCache(); 529 cache.length++; 530 } else if (!cache[q]) { 531 cache.length++; 532 } 533 cache.data[q] = data; 534 }; 535 536 function findPos(obj) { 537 var curleft = obj.offsetLeft || 0; 538 var curtop = obj.offsetTop || 0; 539 while (obj = obj.offsetParent) { 540 curleft += obj.offsetLeft 541 curtop += obj.offsetTop 542 } 543 return {x:curleft,y:curtop}; 544 } 545} 546 547jQuery.fn.autocomplete = function(url, options, data) { 548 // Make sure options exists 549 options = options || {}; 550 // Set url as option 551 options.url = url; 552 // set some bulk local data 553 options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null; 554 555 // Set default values for required options 556 options.inputClass = options.inputClass || "ac_input"; 557 options.resultsClass = options.resultsClass || "ac_results"; 558 options.lineSeparator = options.lineSeparator || "\n"; 559 options.cellSeparator = options.cellSeparator || "|"; 560 options.minChars = options.minChars || 1; 561 options.delay = options.delay || 400; 562 options.matchCase = options.matchCase || 0; 563 options.matchSubset = options.matchSubset || 1; 564 options.matchContains = options.matchContains || 0; 565 options.cacheLength = options.cacheLength || 1; 566 options.mustMatch = options.mustMatch || 0; 567 options.extraParams = options.extraParams || {}; 568 options.loadingClass = options.loadingClass || "ac_loading"; 569 options.selectFirst = options.selectFirst || false; 570 options.selectOnly = options.selectOnly || false; 571 options.maxItemsToShow = options.maxItemsToShow || -1; 572 options.autoFill = options.autoFill || false; 573 options.width = parseInt(options.width, 10) || 0; 574 575 this.each(function() { 576 var input = this; 577 new jQuery.autocomplete(input, options); 578 }); 579 580 // Don't break the chain 581 return this; 582} 583 584jQuery.fn.autocompleteArray = function(data, options) { 585 return this.autocomplete(null, options, data); 586} 587 588jQuery.fn.indexOf = function(e){ 589 for( var i=0; i<this.length; i++ ){ 590 if( this[i] == e ) return i; 591 } 592 return -1; 593};
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.