Autocomplete.hash = {}; Autocomplete.generator = 0; function Autocomplete(source,type,callback) { this.id = Autocomplete.generator++; this.source = source; this.type = type; this.index = -1; this.guesses = []; this.visible = false; this.callback = callback; var scopedThis = this; this.display = document.createElement('div'); this.display.style.position = 'absolute'; this.display.style.display = 'none'; this.display.className = 'autocompleter'; this.display.style.backgroundColor = 'white'; this.display.style.border = '1px solid #42619C'; this.display.style.zIndex = 10; this.display.style.overflow = 'hidden'; this.display.onmouseout = function(event) { scopedThis.clearIndex(); } this.source.onkeydown = function(event) { event = event || window.event; switch(event.keyCode) { case 38: //up scopedThis.setIndex(scopedThis.index-1); return false; case 40: //down scopedThis.setIndex(scopedThis.index+1); return false; case 13: //enter case 9: //tab if(scopedThis.visible) { if(scopedThis.index == -1) { scopedThis.setIndex(0); } if(scopedThis.index != -1) { scopedThis.accept(); if(event.keyCode != 9) { return false; } } } break; } } this.source.onkeyup = function(event) { if(scopedThis.source.value != scopedThis.previous) { scopedThis.setGuesses(scopedThis.guesses); } scopedThis.request(); } this.source.onblur = function(event) { scopedThis.cancel(true); } Autocomplete.hash[this.id] = this; } Autocomplete.prototype = { accept: function() { if(this.index != -1) { var app = this.guesses[this.index]; this.source.value = app.display; //this.source.appId = app.value; this.previous = this.source.value; this.cancel(true); if(this.callback) { this.callback(app); } } }, cancel: function(clear) { if(this.ajax) { this.ajax.cancel(); this.ajax = null; } if(clear) { this.setGuesses([]); } }, request: function() { if(this.source.value == '') { this.previous = ''; this.setGuesses([]); } else if(this.source.value != this.previous) { this.previous = this.source.value; var scopedThis = this; this.cancel(); var ajax = new Ajax("/ajax/autocomplete.php",{ method:'get', data:'type='+encodeURIComponent(this.type)+'&text='+encodeURIComponent(this.source.value), onComplete:function(response){ scopedThis.ajax = null; try { eval("var guesses = "+response); scopedThis.setGuesses(guesses); } catch(E){alert(E.name + ": "+E.message)} } }); ajax.request(); this.ajax = ajax; } }, setGuesses: function(guesses) { if(guesses.length > 0) { this.guesses = guesses; } this.index = -1; var scopedThis = this; var coords = $(this.source).getCoordinates(); this.display.style.left = coords.left + 'px'; this.display.style.top = coords.bottom + 'px'; this.display.style.width = Math.round(coords.width * 1.5) + 'px'; this.display.innerHTML = ''; var displayed = 0; for(var i = 0 ; i < guesses.length ; i++) { var app = guesses[i]; var div = document.createElement('div'); div.style.position = 'relative'; div.style.overflow = 'hidden'; div.style.cursor = 'pointer'; div.style.whiteSpace = 'nowrap'; div.style.width = '100%'; div.innerHTML = ''; if(app.icon){ div.innerHTML += " "; } else { div.innerHTML += " "; } div.innerHTML += ""+app.display+""; eval("div.onmouseover = function(event) {scopedThis.setIndex("+displayed+");};"); eval("div.onmousedown = function(event) {scopedThis.setIndex("+displayed+");scopedThis.accept();};"); div.title = app.display; app.dom = div; this.display.appendChild(div); displayed++; } this.visible = displayed>0; this.display.style.display = this.visible?'':'none'; if(this.display.parentNode != document.body) { document.body.appendChild(this.display); } }, clearIndex: function() { if(this.index != -1) { this.guesses[this.index].dom.style.backgroundColor = ''; } this.index = -1; }, setIndex: function(index) { if(index < 0) { index = 0; } if(index >= this.guesses.length) { index = this.guesses.length - 1; } if(this.index != index) { if(this.index != -1) { this.guesses[this.index].dom.style.backgroundColor = ''; } this.index = index; this.guesses[index].dom.style.backgroundColor = '#DEDFEF'; } } };