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