// Script.aculo.us Autocompleter patch to avoid jumping scrollbars
// original: http://skaue.com/scriptaculous/combobox/
// by Tommy Skaue tommy.skaue@gmail.com

	var MyComboBox = Class.create();
	
	MyComboBox.Autocompleter = Autocompleter.Local;
	
	// Overload onBlur
	MyComboBox.Autocompleter.prototype.onBlur = function(event) {
		if (Element.getStyle(this.update, 'display') == 'none') { return; }
		// Dont hide the div on "blur" if the user clicks scrollbar
		if(event&&Element.getStyle(this.update, 'height') != ''){
			if( Position.within( this.update , Event.pointerX(event) , Event.pointerY(event) ) ){ 
				Event.observe(this.update, "blur", this.onBlur.bindAsEventListener(this),true); // make sure blur is still around on
				return; 
			}
		}
		// needed to make click events working
		setTimeout(this.hide.bind(this), 250);
		this.hasFocus = false;
		this.active = false; 
	}
	
	MyComboBox.Autocompleter.prototype.markPrevious = function() {
		if(this.index > 0) this.index--
		else this.index = this.entryCount-1;
		this.getEntry(this.index).scrollIntoView(false);
	}
	
	// Overload selector
	MyComboBox.selector = function(instance) {
		var ret       = []; // Beginning matches
		var partial   = []; // Inside matches
		var entry     = instance.getToken();
		var count     = 0;

		// hack options - single char --> fullSearch OFF, capitalized char --> ignoreCase OFF
		instance.options.fullSearch = (entry.length==1?false:true)
			
		for (var i = 0; i < instance.options.array.length &&  
			ret.length < instance.options.choices ; i++) { 
			
			var elem = instance.options.array[i];
			var foundPos = instance.options.ignoreCase ? 
				elem.toLowerCase().indexOf(entry.toLowerCase()) : 
				elem.indexOf(entry);
			var delimPos = elem.indexOf(' ');

			while (foundPos != -1) {
				if (foundPos == 0 && elem.length != entry.length) { 
					ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + 
						elem.substr(entry.length) + "</li>");
					break;
				} else if (entry.length >= instance.options.partialChars && 
					instance.options.partialSearch && foundPos != -1) {
					if (entry.length==1 && foundPos == delimPos+1){ // skip singlechar search beyond whitespace
						partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
							elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
							foundPos + entry.length) + "</li>");
						break;
					} else if (instance.options.fullSearch || entry.length!=1 && /\s/.test(elem.substr(foundPos-1,1))) {
						partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
							elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
							foundPos + entry.length) + "</li>");
						break;
					}
				}

				foundPos = instance.options.ignoreCase ? 
					elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
					elem.indexOf(entry, foundPos + 1);

			}
        }
		if (partial.length)
			ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
        return "<ul>" + ret.join('')  + (ret.length==0?'<li class="disabled"><strong>Not found "'+entry+'"!</strong></li>':'') +  "</ul>";
      }
	
	MyComboBox.prototype = {
		initialize: function(textElement, resultsElement, array, options) {
		this.textElement = $(textElement);
		
		// the first text box inside the container
		this.textBox = $A( this.textElement.getElementsByTagName('INPUT') ).findAll( function(input) {
			return (input.getAttribute('type') == 'text');
		})[0];
		
		
		//Cache for allChoicesHtml
		this.allChoicesHtml = null
		
		this.results = $(resultsElement);
		
		// we dynamically insert a SPAN that will serve as the drop-down 'list_opener'
	/*	this.list_opener = Element.extend(Builder.node('a'));
		
		this.list_opener.setAttribute('className','combo_list_opener');
		this.list_opener.setAttribute('href','javascript:void(0)');
		this.list_opener.setAttribute('title','Open list');
		this.list_opener.innerHTML = '&raquo;';
		this.textElement.appendChild(this.list_opener);*/
		this.array = array;
		
		this.results.style.display 	= 'none';
		
		this.events = {
			showChoices: 	this.showChoices.bindAsEventListener(this),
			hideChoices: 	this.hideChoices.bindAsEventListener(this),
			click:			this.click.bindAsEventListener(this),
			keyDown:		this.keyDown.bindAsEventListener(this)
		}
		
		this.autocompleter = new MyComboBox.Autocompleter(this.textBox, this.results, this.array, options);
		
		//Event.observe(this.list_opener, 'click', this.events.click);
		Event.observe(this.textBox, 'click', this.events.click);
		Event.observe(this.textBox, 'keydown', this.events.keyDown);
		},
		
		getAllChoices: function(e) {
			if(!this.allChoicesHtml){
				var choices = this.array.collect( function(choice) { return '<li>' + choice + '</li>'; } ).slice(0, 200);;
				this.allChoicesHtml = '<ul>' + choices.join('') + (this.array.length>100?'<li class="disabled"><strong>Limited to the first 200 items!</strong></li>':'') + '</ul>';
			}
			this.autocompleter.updateChoices(this.allChoicesHtml);
		},
		
		keyDown: function(e) {
			if (e.keyCode == Event.KEY_DOWN && this.choicesVisible() ) {
				this.showChoices();
			}			
		},
		
		// returns boolean indicating whether the choices are displayed
		choicesVisible: function() { return (Element.getStyle(this.autocompleter.update, 'display') == 'none'); },
		
		click: function() {
			if (this.choicesVisible() ) {
				this.showChoices();
			} else {
				this.hideChoices();
			}
		},
			
		showChoices: function() {
			this.textBox.focus();
			this.autocompleter.changed = false;
			this.autocompleter.hasFocus = true;
			this.getAllChoices();
		},
		
		hideChoices: function() {
			this.autocompleter.onBlur();
		}
		

	}
	
