1 2(function($) { 3 4// some key codes 5 var RETURN = 13; 6 var TAB = 9; 7 var ESC = 27; 8 var ARRLEFT = 37; 9 var ARRUP = 38; 10 var ARRRIGHT = 39; 11 var ARRDOWN = 40; 12 var BACKSPACE = 8; 13 var DELETE = 46; 14 15function debug(s){ 16 $('#info').append(htmlspecialchars(s)+'<br>'); 17} 18// getting caret position obj: {start,end} 19function getCaretPosition(obj){ 20 var start = -1; 21 var end = -1; 22 if(typeof obj.selectionStart != "undefined"){ 23 start = obj.selectionStart; 24 end = obj.selectionEnd; 25 } 26 else if(document.selection&&document.selection.createRange){ 27 var M=document.selection.createRange(); 28 var Lp; 29 try{ 30 Lp = M.duplicate(); 31 Lp.moveToElementText(obj); 32 }catch(e){ 33 Lp=obj.createTextRange(); 34 } 35 Lp.setEndPoint("EndToStart",M); 36 start=Lp.text.length; 37 if(start>obj.value.length) 38 start = -1; 39 40 Lp.setEndPoint("EndToStart",M); 41 end=Lp.text.length; 42 if(end>obj.value.length) 43 end = -1; 44 } 45 return {'start':start,'end':end}; 46} 47// set caret to 48function setCaret(obj,l){ 49 obj.focus(); 50 if (obj.setSelectionRange){ 51 obj.setSelectionRange(l,l); 52 } 53 else if(obj.createTextRange){ 54 m = obj.createTextRange(); 55 m.moveStart('character',l); 56 m.collapse(); 57 m.select(); 58 } 59} 60// prepare array with velued objects 61// required properties are id and value 62// rest of properties remaines 63function prepareArray(jsondata){ 64 var new_arr = []; 65 for(var i=0;i<jsondata.length;i++){ 66 if(jsondata[i].id != undefined && jsondata[i].value != undefined){ 67 jsondata[i].id = jsondata[i].id+""; 68 jsondata[i].value = jsondata[i].value+""; 69 if(jsondata[i].info != undefined) 70 jsondata[i].info = jsondata[i].info+""; 71 new_arr.push(jsondata[i]); 72 } 73 } 74 return new_arr; 75} 76// php analogs 77function escapearg(s){ 78 if(s == undefined || !s) return ''; 79 return s.replace('\\','\\\\'). 80 replace('*','\\*'). 81 replace('.','\\.'). 82 replace('/','\\/'); 83} 84function htmlspecialchars(s){ 85 if(s == undefined || !s) return ''; 86 return s.replace('&','&'). 87 replace('<','<'). 88 replace('>','>'); 89} 90function ltrim(s){ 91 if(s == undefined || !s) return ''; 92 return s.replace(/^\s+/g,''); 93} 94 95// extending jQuery 96$.fn.autocomplete = function(options){ return this.each(function(){ 97 // take me 98 var me = $(this); 99 var me_this = $(this).get(0); 100 101 // test for supported text elements 102 if(!me.is('input:text,input:password,textarea')) 103 return; 104 105 // get or ajax_get required! 106 if(!options && (!$.isFunction(options.get) || !options.ajax_get)){ 107 return; 108 } 109 // check plugin enabled 110 if(me.attr('jqac') == 'on') return; 111 112 // plugin on! 113 me.attr('jqac','on'); 114 115 // no browser's autocomplete! 116 me.attr('autocomplete','off'); 117 118 // default options 119 options = $.extend({ 120 delay : 250 , 121 timeout : 15000 , 122 minchars : 3 , 123 multi : false , 124 cache : true , 125 height : 250 , 126 autowidth : false , 127 noresults : 'no result' 128 }, 129 options); 130 131 // bind key events 132 // handle special keys here 133 me.keydown(function(ev){ 134 switch(ev.which){ 135 // return choose highlighted item or default propogate 136 case RETURN: 137 if(!suggestions_menu) return true; 138 else setHighlightedValue(); 139 return false; 140 // escape clears menu 141 case ESC: 142 clearSuggestions(); 143 return false; 144 } 145 return true; 146 }); 147 me.keypress(function(ev){ 148 // ev.which doesn't work here - it always returns 0 149 switch(ev.keyCode){ 150 case RETURN: case ESC: 151 return false; 152 // up changes highlight 153 case ARRUP: 154 changeHighlight(ev.keyCode); 155 return false; 156 // down changes highlight or open new menu 157 case ARRDOWN: 158 if(!suggestions_menu) getSuggestions(getUserInput()); 159 else changeHighlight(ev.keyCode); 160 return false; 161 } 162 return true; 163 }); 164 // handle normal characters here 165 me.keyup(function(ev) { 166 switch(ev.which) { 167 case RETURN: case ESC: case ARRLEFT: case ARRRIGHT: case ARRUP: case ARR
167DOWN: 168 return false; 169 default: 170 getSuggestions(getUserInput()); 171 } 172 return true; 173 }); 174 175 // init variables 176 var user_input = ""; 177 var input_chars_size = 0; 178 var suggestions = []; 179 var current_highlight = 0; 180 var suggestions_menu = false; 181 var suggestions_list = false; 182 var loading_indicator = false; 183 var clearSuggestionsTimer = false; 184 var getSuggestionsTimer = false; 185 var showLoadingTimer = false; 186 var zIndex = me.css('z-index'); 187 188 // get user input 189 function getUserInput(){ 190 var val = me.val(); 191 if(options.multi){ 192 var pos = getCaretPosition(me_this); 193 var start = pos.start; 194 for(;start>0 && val.charAt(start-1) != ',';start--){} 195 var end = pos.start; 196 for(;end<val.length && val.charAt(end) != ',';end++){} 197 var val = val.substr(start,end-start); 198 } 199 return ltrim(val); 200 } 201 // set suggestion 202 function setSuggestion(val){ 203 user_input = val; 204 if(options.multi){ 205 var orig = me.val(); 206 var pos = getCaretPosition(me_this); 207 var start = pos.start; 208 for(;start>0 && orig.charAt(start-1) != ',';start--){} 209 var end = pos.start; 210 for(;end<orig.length && orig.charAt(end) != ',';end++){} 211 var new_val = orig.substr(0,start) + (start>0?' ':'') + val + orig.substr(end); 212 me.val(new_val); 213 setCaret(me_this,start + val.length + (start>0?1:0)); 214 } 215 else{ 216 me_this.focus(); 217 me.val(val); 218 } 219 } 220 // get suggestions 221 function getSuggestions(val){ 222 // input length is less than the min required to trigger a request 223 // reset input string 224 // do nothing 225 if (val.length < options.minchars){ 226 clearSuggestions(); 227 return false; 228 } 229 // if caching enabled, and user is typing (ie. length of input is increasing) 230 // filter results out of suggestions from last request 231 if (options.cache && val.length > input_chars_size && suggestions.length){ 232 var arr = []; 233 for (var i=0;i<suggestions.length;i++){ 234 var re = new RegExp("("+escapearg(val)+")",'ig'); 235 if(re.exec(suggestions[i].value)) 236 arr.push( suggestions[i] ); 237 } 238 user_input = val; 239 input_chars_size = val.length; 240 suggestions = arr; 241 createList(suggestions); 242 return false; 243 } 244 else{// do new request 245 clearTimeout(getSuggestionsTimer); 246 user_input = val; 247 input_chars_size = val.length; 248 getSuggestionsTimer = setTimeout( 249 function(){ 250 suggestions = []; 251 // call pre callback, if exists 252 if($.isFunction(options.pre_callback)) 253 options.pre_callback(); 254 // call get 255 if($.isFunction(options.get)){ 256 suggestions = prepareArray(options.get(val)); 257 createList(suggestions); 258 } 259 // call AJAX get 260 else if($.isFunction(options.ajax_get)){ 261 clearSuggestions(); 262 showLoadingTimer = setTimeout(show_loading,options.delay); 263 options.ajax_get(val,ajax_continuation); 264 } 265 }, 266 options.delay ); 267 } 268 return false; 269 }; 270 // AJAX continuation 271 function ajax_continuation(jsondata){ 272 hide_loading(); 273 suggestions = prepareArray(jsondata); 274 createList(suggestions); 275 } 276 // shows loading indicator 277 function show_loading(){ 278 if(!loading_indicator){ 279 loading_indicator = $('<div class="jqac-menu"><div class="jqac-loading"></div></div>').get(0); 280 $(loading_indicator).css('position','absolute'); 281 var pos = me.offset(); 282 $(loading_indicator).css('left', pos.left + "px"); 283 $(loading_indicator).css('top', ( pos.top + me.height() + 2 ) + "px"); 284 if(!options.autowidth) 285 $(loading_indicator).width(me.width()); 286 $('body').append(loading_indicator); 287 } 288 $(loading_indicator).show(); 289 setTimeout(hide_loading,10000); 290 } 291 // hides loading indicator 292 function hide_loading(){ 293 if(loading_indicator) 294 $(loading_indicator).hide(); 295 clearTimeout(showLoadingTimer); 296 } 297 // create suggestions list 298 function createList(arr){ 299 if(suggestions_menu) 300 $(suggestions_menu).remove(); 301 hide_loading();
302 killTimeout(); 303 304 // create holding div 305 suggestions_menu = $('<div class="jqac-menu"></div>').get(0); 306 307 // ovveride some necessary CSS properties 308 $(suggestions_menu).css({'position':'absolute', 309 'z-index':zIndex, 310 'max-height':options.height+'px', 311 'overflow-y':'auto'}); 312 313 // create and populate ul 314 suggestions_list = $('<ul class="dropdown"></ul>').get(0); 315 // set some CSS's 316 $(suggestions_list). 317 css('list-style','none'). 318 css('margin','0px'). 319 css('padding','2px'). 320 css('overflow','hidden'); 321 // regexp for replace 322 var re = new RegExp("("+escapearg(htmlspecialchars(user_input))+")",'ig'); 323 // loop throught arr of suggestions creating an LI element for each suggestion 324 for (var i=0;i<arr.length;i++){ 325 var val = new String(arr[i].value); 326 // using RE 327 var output = htmlspecialchars(val).replace(re,'<em>$1</em>'); 328 // using substr 329 //var st = val.toLowerCase().indexOf( user_input.toLowerCase() ); 330 //var len = user_input.length; 331 //var output = val.substring(0,st)+"<em>"+val.substring(st,st+len)+"</em>"+val.substring(st+len); 332 333 var span = $('<span class="jqac-link">'+output+'</span>').get(0); 334 if (arr[i].info != undefined && arr[i].info != ""){ 335 $(span).append($('<div class="jqac-info">'+arr[i].info+'</div>')); 336 } 337 338 $(span).attr('name',i+1); 339 $(span).click(function () { setHighlightedValue(); }); 340 $(span).mouseover(function () { setHighlight($(this).attr('name'),true); }); 341 342 var li = $('<li></li>').get(0); 343 $(li).append(span); 344 345 $(suggestions_list).append(li); 346 } 347 348 // no results 349 if (arr.length == 0){ 350 $(suggestions_list).append('<li class="jqac-warning">'+options.noresults+'</li>'); 351 } 352 353 $(suggestions_menu).append(suggestions_list); 354 355 // get position of target textfield 356 // position holding div below it 357 // set width of holding div to width of field 358 var pos = me.offset(); 359 360 $(suggestions_menu).css('left', pos.left + "px"); 361 $(suggestions_menu).css('top', ( pos.top + me.height() + 2 ) + "px"); 362 if(!options.autowidth) 363 $(suggestions_menu).width(me.width()); 364 365 // set mouseover functions for div 366 // when mouse pointer leaves div, set a timeout to remove the list after an interval 367 // when mouse enters div, kill the timeout so the list won't be removed 368 $(suggestions_menu).mouseover(function(){ killTimeout() }); 369 $(suggestions_menu).mouseout(function(){ resetTimeout() }); 370 371 // add DIV to document 372 $('body').append(suggestions_menu); 373 374 // bgIFRAME support 375 if($.fn.bgiframe) 376 $(suggestions_menu).bgiframe({height: suggestions_menu.scrollHeight}); 377 378 379 // adjust height: add +20 for scrollbar 380 if(suggestions_menu.scrollHeight > options.height){ 381 $(suggestions_menu).height(options.height); 382 $(suggestions_menu).width($(suggestions_menu).width()+20); 383 } 384 385 // currently no item is highlighted 386 current_highlight = 0; 387 388 // remove list after an interval 389 clearSuggestionsTimer = setTimeout(function () { clearSuggestions() }, options.timeout); 390 }; 391 // set highlighted value 392 function setHighlightedValue(){ 393 if(current_highlight && suggestions[current_highlight-1]){ 394 var sugg = suggestions[ current_highlight-1 ]; 395 if(sugg.affected_value != undefined && sugg.affected_value != '') 396 setSuggestion(sugg.affected_value); 397 else 398 setSuggestion(sugg.value); 399 // pass selected object to callback function, if exists 400 if ($.isFunction(options.callback)) 401 options.callback( suggestions[current_highlight-1] ); 402 403 clearSuggestions(); 404 } 405 }; 406 // change highlight according to key 407 function changeHighlight(key){ 408 if(!suggestions_list || suggestions.length == 0) return false; 409 var n; 410 if (key == ARRDOWN) 411 n = current_highlight + 1; 412 else if (key == ARRUP) 413 n = current_highlight - 1; 414 415 if (n > $(suggestions_list).children().size()) 416 n = 1; 417 if (n < 1) 418 n = $(suggestions_list).children().size(); 419 setHighlight(n); 420 }; 421 // change highlight 422 function setHighlight(n,mouse_mode){ 423 if (!suggestions_list) return false;
424 if (current_highlight > 0) clearHighlight(); 425 current_highlight = Number(n); 426 var li = $(suggestions_list).children().get(current_highlight-1); 427 li.className = 'jqac-highlight'; 428 // for mouse mode don't adjust scroll! prevent scrolling jumps 429 if(!mouse_mode) adjustScroll(li); 430 killTimeout(); 431 }; 432 // clear highlight 433 function clearHighlight(){ 434 if (!suggestions_list)return false; 435 if (current_highlight > 0){ 436 $(suggestions_list).children().get(current_highlight-1).className = ''; 437 current_highlight = 0; 438 } 439 }; 440 // clear suggestions list 441 function clearSuggestions(){ 442 killTimeout(); 443 if(suggestions_menu){ 444 $(suggestions_menu).remove(); 445 suggestions_menu = false; 446 suggestions_list = false; 447 current_highlight = 0; 448 } 449 }; 450 // set scroll 451 function adjustScroll(el){ 452 if(!suggestions_menu) return false; 453 var viewportHeight = suggestions_menu.clientHeight; 454 var wholeHeight = suggestions_menu.scrollHeight; 455 var scrolled = suggestions_menu.scrollTop; 456 var elTop = el.offsetTop; 457 var elBottom = elTop + el.offsetHeight; 458 if(elBottom > scrolled + viewportHeight){ 459 suggestions_menu.scrollTop = elBottom - viewportHeight; 460 } 461 else if(elTop < scrolled){ 462 suggestions_menu.scrollTop = elTop; 463 } 464 return true; 465 } 466 // timeout funcs 467 function killTimeout(){ 468 clearTimeout(clearSuggestionsTimer); 469 }; 470 function resetTimeout(){ 471 clearTimeout(clearSuggestionsTimer); 472 clearSuggestionsTimer = setTimeout(function () { clearSuggestions() }, 1000); 473 }; 474 475})}; 476 477})($);
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.