1/* 2-------------------------------------------------------- 3suggest.js - Input Suggest 4Version 2.3.1 (Update 2013/02/11) 5Copyright (c) 2006-2013 onozaty (http://www.enjoyxstudy.com) 6Released under an MIT-style license. 7For details, see the web site: 8 http://www.enjoyxstudy.com/javascript/suggest/ 9-------------------------------------------------------- 10*/ 11 12if (!Suggest) { 13 var Suggest = {}; 14} 15/*-- KeyCodes -----------------------------------------*/ 16Suggest.Key = { 17 TAB: 9, 18 RETURN: 13, 19 ESC: 27, 20 UP: 38, 21 DOWN: 40 22}; 23 24/*-- Utils --------------------------------------------*/ 25Suggest.copyProperties = function(dest, src) { 26 for (var property in src) { 27 dest[property] = src[property]; 28 } 29 return dest; 30}; 31 32/*-- Suggest.Local ------------------------------------*/ 33Suggest.Local = function() { 34 this.initialize.apply(this, arguments); 35}; 36Suggest.Local.prototype = { 37 initialize: function(input, suggestArea, candidateList) { 38 39 this.input = this._getElement(input); 40 this.suggestArea = this._getElement(suggestArea); 41 this.candidateList = candidateList; 42 this.oldText = this.getInputText(); 43 44 if (arguments[3]) this.setOptions(arguments[3]); 45 46 // reg event 47 this._addEvent(this.input, 'focus', this._bind(this.checkLoop)); 48 this._addEvent(this.input, 'blur', this._bind(this.inputBlur)); 49 this._addEvent(this.suggestArea, 'blur', this._bind(this.inputBlur)); 50 51 this._addEvent(this.input, 'keydown', this._bindEvent(this.keyEvent)); 52 53 // init 54 this.clearSuggestArea(); 55 }, 56 57 // options 58 interval: 500, 59 dispMax: 20, 60 listTagName: 'div', 61 prefix: false, 62 ignoreCase: true, 63 highlight: false, 64 dispAllKey: false, 65 classMouseOver: 'over', 66 classSelect: 'select', 67 hookBeforeSearch: function(){}, 68 69 setOptions: function(options) { 70 Suggest.copyProperties(this, options); 71 }, 72 73 inputBlur: function() { 74 75 setTimeout(this._bind(function(){ 76 77 if (document.activeElement == this.suggestArea 78 || document.activeElement == this.input) { 79 // keep suggestion 80 return; 81 } 82 83 this.changeUnactive(); 84 this.oldText = this.getInputText(); 85 86 if (this.timerId) clearTimeout(this.timerId); 87 this.timerId = null; 88 89 setTimeout(this._bind(this.clearSuggestArea), 500); 90 }, 500)); 91 }, 92 93 checkLoop: function() { 94 var text = this.getInputText(); 95 if (text != this.oldText) { 96 this.oldText = text; 97 this.search(); 98 } 99 if (this.timerId) clearTimeout(this.timerId); 100 this.timerId = setTimeout(this._bind(this.checkLoop), this.interval); 101 }, 102 103 search: function() { 104 105 // init 106 this.clearSuggestArea(); 107 108 var text = this.getInputText(); 109 110 if (text == '' || text == null) return; 111 112 this.hookBeforeSearch(text); 113 var resultList = this._search(text); 114 if (resultList.length != 0) this.createSuggestArea(resultList); 115 }, 116 117 _search: function(text) { 118 119 var resultList = []; 120 var temp; 121 this.suggestIndexList = []; 122 123 for (var i = 0, length = this.candidateList.length; i < length; i++) { 124 if ((temp = this.isMatch(this.candidateList[i], text)) != null) { 125 resultList.push(temp); 126 this.suggestIndexList.push(i); 127 128 if (this.dispMax != 0 && resultList.length >= this.dispMax) break; 129 } 130 } 131 return resultList; 132 }, 133 134 isMatch: function(value, pattern) { 135 136 if (value == null) return null; 137 138 var pos = (this.ignoreCase) ? 139 value.toLowerCase().indexOf(pattern.toLowerCase()) 140 : value.indexOf(pattern); 141 142 if ((pos == -1) || (this.prefix && pos != 0)) return null; 143 144 if (this.highlight) { 145 return (this._escapeHTML(value.substr(0, pos)) + '<strong>' 146 + this._escapeHTML(value.substr(pos, pattern.length)) 147 + '</strong>' + this._escapeHTML(value.substr(pos + pattern.length))); 148 } else { 149 return this._escapeHTML(value); 150 } 151 }, 152 153 clearSuggestArea: function() { 154 this.suggestArea.innerHTML = ''; 155 this.suggestArea.style.display = 'none'; 156 this.suggestList = null; 157 this.suggestIndexList = null; 158 this.activePosition = null; 159 }, 160 161 createSuggestArea: function(resultList) { 162 163 this.suggestList = []; 164 this.inputValueBackup = this.input.value; 165 166 for (var i = 0, length = resultList.length; i < length; i++) { 167 var element = document.createElement(this.listTagName); 168 element.innerHTML = resultList[i]; 169 this.suggestArea.appendChild(element); 170 171 this._addEvent(element, 'click', this._bindEvent(this.listClick, i)); 172 this._addEvent(element, 'mouseover', this._bindEvent(this.listMouseOver, i)); 173 this._addEvent(element, 'mouseout', this._bindEvent(this.listMouseOut, i)); 174 175 this.suggestList.push(element); 176 } 177 178 this.suggestArea.style.display = ''; 179 this.suggestArea.scrollTop = 0; 180 }, 181 182 getInputText: function() { 183 return this.input.value; 184 }, 185 186 setInputText: function(text) { 187 this.input.value = text; 188 }, 189 190 // key event 191 keyEvent: function(event) { 192 193 if (!this.timerId) { 194 this.timerId = setTimeout(this._bind(this.checkLoop), this.interval); 195 } 196 197 if (this.dispAllKey && event.ctrlKey 198 && this.getInputText() == '' 199 && !this.suggestList 200 && event.keyCode == Suggest.Key.DOWN) { 201 // dispAll 202 this._stopEvent(event); 203 this.keyEventDispAll(); 204 } else if (event.keyCode == Suggest.Key.UP || 205 event.keyCode == Suggest.Key.DOWN) { 206 // key move 207 if (this.suggestList && this.suggestList.length != 0) { 208 this._stopEvent(event); 209 this.keyEventMove(event.keyCode); 210 } 211 } else if (event.keyCode == Suggest.Key.RETURN) { 212 // fix 213 if (this.suggestList && this.suggestList.length != 0) { 214 this._stopEvent(event); 215 this.keyEventReturn(); 216 } 217 } else if (event.keyCode == Suggest.Key.ESC) { 218 // cancel 219 if (this.suggestList && this.suggestList.length != 0) { 220 this._stopEvent(event); 221 this.keyEventEsc(); 222 } 223 } else { 224 this.keyEventOther(event); 225 } 226 }, 227 228 keyEventDispAll: function() { 229 230 // init 231 this.clearSuggestArea(); 232 233 this.oldText = this.getInputText(); 234 235 this.suggestIndexList = []; 236 for (var i = 0, length = this.candidateList.length; i < length; i++) { 237 this.suggestIndexList.push(i); 238 } 239 240 this.createSuggestArea(this.candidateList); 241 }, 242 243 keyEventMove: function(keyCode) { 244 245 this.changeUnactive(); 246 247 if (keyCode == Suggest.Key.UP) { 248 // up 249 if (this.activePosition == null) { 250 this.activePosition = this.suggestList.length -1; 251 }else{ 252 this.activePosition--; 253 if (this.activePosition < 0) { 254 this.activePosition = null; 255 this.input.value = this.inputValueBackup; 256 this.suggestArea.scrollTop = 0; 257 return; 258 } 259 } 260 }else{ 261 // down 262 if (this.activePosition == null) { 263 this.activePosition = 0; 264 }else{ 265 this.activePosition++; 266 } 267 268 if (this.activePosition >= this.suggestList.length) { 269 this.activePosition = null; 270 this.input.value = this.inputValueBackup; 271 this.suggestArea.scrollTop = 0; 272 return; 273 } 274 } 275 276 this.changeActive(this.activePosition); 277 }, 278 279 keyEventReturn: function() { 280 281 this.clearSuggestArea(); 282 this.moveEnd(); 283 }, 284 285 keyEventEsc: function() { 286 287 this.clearSuggestArea(); 288 this.input.value = this.inputValueBackup; 289 this.oldText = this.getInputText(); 290 291 if (window.opera) setTimeout(this._bind(this.moveEnd), 5); 292 }, 293 294 keyEventOther: function(event) {}, 295 296 changeActive: function(index) { 297 298 this.setStyleActive(this.suggestList[index]); 299 300 this.setInputText(this.candidateList[this.suggestIndexList[index]]); 301 302 this.oldText = this.getInputText(); 303 this.input.focus(); 304 }, 305 306 changeUnactive: function() { 307 308 if (this.suggestList != null 309 && this.suggestList.length > 0 310 && this.activePosition != null) { 311 this.setStyleUnactive(this.suggestList[this.activePosition]); 312 } 313 }, 314 315 listClick: function(event, index) { 316 317 this.changeUnactive(); 318 this.activePosition = index; 319 this.changeActive(index); 320 321 this.clearSuggestArea(); 322 this.moveEnd(); 323 }, 324 325 listMouseOver: function(event, index) { 326 this.setStyleMouseOver(this._getEventElement(event)); 327 }, 328 329 listMouseOut: function(event, index) { 330 331 if (!this.suggestList) return; 332 333 var element = this._getEventElement(event); 334 335 if (index == this.activePosition) { 336 this.setStyleActive(element); 337 }else{ 338 this.setStyleUnactive(element); 339 } 340 }, 341 342 setStyleActive: function(element) { 343 element.className = this.classSelect; 344 345 // auto scroll 346 var offset = element.offsetTop; 347 var offsetWithHeight = offset + element.clientHeight; 348 349 if (this.suggestArea.scrollTop > offset) { 350 this.suggestArea.scrollTop = offset 351 } else if (this.suggestArea.scrollTop + this.suggestArea.clientHeight < offsetWithHeight) { 352 this.suggestArea.scrollTop = offsetWithHeight - this.suggestArea.clientHeight; 353 } 354 }, 355 356 setStyleUnactive: function(element) { 357 element.className = ''; 358 }, 359 360 setStyleMouseOver: function(element) { 361 element.className = this.classMouseOver; 362 }, 363 364 moveEnd: function() { 365 366 if (this.input.createTextRange) { 367 this.input.focus(); // Opera 368 var range = this.input.createTextRange(); 369 range.move('character', this.input.value.length); 370 range.select(); 371 } else if (this.input.setSelectionRange) { 372 this.input.setSelectionRange(this.input.value.length, this.input.value.length); 373 } 374 }, 375 376 // Utils 377 _getElement: function(element) { 378 return (typeof element == 'string') ? document.getElementById(element) : element; 379 }, 380 _addEvent: (window.addEventListener ? 381 function(element, type, func) { 382 element.addEventListener(type, func, false); 383 } : 384 function(element, type, func) { 385 element.attachEvent('on' + type, func); 386 }), 387 _stopEvent: function(event) { 388 if (event.preventDefault) { 389 event.preventDefault(); 390 event.stopPropagation(); 391 } else { 392 event.returnValue = false;
393 event.cancelBubble = true; 394 } 395 }, 396 _getEventElement: function(event) { 397 return event.target || event.srcElement; 398 }, 399 _bind: function(func) { 400 var self = this; 401 var args = Array.prototype.slice.call(arguments, 1); 402 return function(){ func.apply(self, args); }; 403 }, 404 _bindEvent: function(func) { 405 var self = this; 406 var args = Array.prototype.slice.call(arguments, 1); 407 return function(event){ event = event || window.event; func.apply(self, [event].concat(args)); }; 408 }, 409 _escapeHTML: function(value) { 410 return value.replace(/\&/g, '&').replace( /</g, '<').replace(/>/g, '>') 411 .replace(/\"/g, '"').replace(/\'/g, '''); 412 } 413}; 414 415/*-- Suggest.LocalMulti ---------------------------------*/ 416Suggest.LocalMulti = function() { 417 this.initialize.apply(this, arguments); 418}; 419Suggest.copyProperties(Suggest.LocalMulti.prototype, Suggest.Local.prototype); 420 421Suggest.LocalMulti.prototype.delim = ' '; // delimiter 422 423Suggest.LocalMulti.prototype.keyEventReturn = function() { 424 425 this.clearSuggestArea(); 426 this.input.value += this.delim; 427 this.moveEnd(); 428}; 429 430Suggest.LocalMulti.prototype.keyEventOther = function(event) { 431 432 if (event.keyCode == Suggest.Key.TAB) { 433 // fix 434 if (this.suggestList && this.suggestList.length != 0) { 435 this._stopEvent(event); 436 437 if (!this.activePosition) { 438 this.activePosition = 0; 439 this.changeActive(this.activePosition); 440 } 441 442 this.clearSuggestArea(); 443 this.input.value += this.delim; 444 if (window.opera) { 445 setTimeout(this._bind(this.moveEnd), 5); 446 } else { 447 this.moveEnd(); 448 } 449 } 450 } 451}; 452 453Suggest.LocalMulti.prototype.listClick = function(event, index) { 454 455 this.changeUnactive(); 456 this.activePosition = index; 457 this.changeActive(index); 458 459 this.input.value += this.delim; 460 461 this.clearSuggestArea(); 462 this.moveEnd(); 463}; 464 465Suggest.LocalMulti.prototype.getInputText = function() { 466 467 var pos = this.getLastTokenPos(); 468 469 if (pos == -1) { 470 return this.input.value; 471 } else { 472 return this.input.value.substr(pos + 1); 473 } 474}; 475 476Suggest.LocalMulti.prototype.setInputText = function(text) { 477 478 var pos = this.getLastTokenPos(); 479 480 if (pos == -1) { 481 this.input.value = text; 482 } else { 483 this.input.value = this.input.value.substr(0 , pos + 1) + text; 484 } 485}; 486 487Suggest.LocalMulti.prototype.getLastTokenPos = function() { 488 return this.input.value.lastIndexOf(this.delim); 489};
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.