

	Autocompleter.BaseEnhanced = Class.create(Autocompleter.Base, {
		/*
		 * Aletered to fix Mozillas weird way of handling keydown/keypress events
		 */
		baseInitialize: function(element, update, options) {
		    element          = $(element);
		    this.element     = element;
		    this.update      = $(update);
		    this.hasFocus    = false;
		    this.changed     = false;
		    this.active      = false;
		    this.index       = 0;
		    this.entryCount  = 0;
		    this.oldElementValue = this.element.value;

		    if(this.setOptions)
		      this.setOptions(options);
		    else
		      this.options = options || { };

		    this.options.paramName    = this.options.paramName || this.element.name;
		    this.options.tokens       = this.options.tokens || [];
		    this.options.frequency    = this.options.frequency || 0.4;
		    this.options.minChars     = this.options.minChars || 1;
		    this.options.onShow       = this.options.onShow ||
		      function(element, update){
		        if(!update.style.position || update.style.position=='absolute') {
		          update.style.position = 'absolute';
		          Position.clone(element, update, {
		            setHeight: false,
		            offsetTop: element.offsetHeight
		          });
		        }
		        Effect.Appear(update,{duration:0.15});
		      };
		    this.options.onHide = this.options.onHide ||
		      function(element, update){ new Effect.Fade(update,{duration:0.15}) };

		    if(typeof(this.options.tokens) == 'string')
		      this.options.tokens = new Array(this.options.tokens);
		    // Force carriage returns as token delimiters anyway
		    if (!this.options.tokens.include('\n'))
		      this.options.tokens.push('\n');

		    this.observer = null;

		    this.element.setAttribute('autocomplete','off');

		    Element.hide(this.update);

		    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
			
			/* Fix is here */
			if(Prototype.Browser.Gecko)
				Event.observe(this.element, 'keypress', this.onKeyPress.bindAsEventListener(this));
			else
		    	Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
		  },
		
		/*
		 * Altered to stop weird scroll bug
		 */
		markPrevious: function() {
			if(this.index > 0) this.index--;
			else this.index = this.entryCount-1;
		    // this.getEntry(this.index).scrollIntoView(true);
		},
	
		markNext: function() {
		    if(this.index < this.entryCount-1) this.index++;
		      else this.index = 0;
		    // this.getEntry(this.index).scrollIntoView(false);
		},

		/*
		 * Altered to fix the observer timeout. The default one causes tags to be badly written if you type too fast 
		 */
		onKeyPress: function(event) {
		    if(this.active)
		      switch(event.keyCode) {
		       case Event.KEY_TAB:
		       case Event.KEY_RETURN:
		         this.selectEntry();
		         Event.stop(event);
		       case Event.KEY_ESC:
		         this.hide();
		         this.active = false;
		         Event.stop(event);
		         return;
		       case Event.KEY_LEFT:
		       case Event.KEY_RIGHT:
		         return;
		       case Event.KEY_UP:
		         this.markPrevious();
		         this.render();
		         Event.stop(event);
		         return;
		       case Event.KEY_DOWN:
		         this.markNext();
		         this.render();
		         Event.stop(event);
		         return;
		      }
		     else 
		       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
		         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;

		    this.changed = true;
		    this.hasFocus = true;

		    if(this.observer) clearTimeout(this.observer);
		      this.observer = 
		        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*100);
		}
	});


	Autocompleter.LocalEnhanced = Class.create(Autocompleter.BaseEnhanced, {
	  initialize: function(element, update, array, options) {
	    this.baseInitialize(element, update, options);
	    this.options.array = array;
	  },

	  getUpdatedChoices: function() {
	    this.updateChoices(this.options.selector(this));
	  },

	  setOptions: function(options) {
	    this.options = Object.extend({
	      choices: 10,
	      partialSearch: true,
	      partialChars: 2,
	      ignoreCase: true,
	      fullSearch: false,
	      selector: function(instance) {
	        var ret       = []; // Beginning matches
	        var partial   = []; // Inside matches
	        var entry     = instance.getToken();
	        var count     = 0;

	        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);

	          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 (instance.options.fullSearch || /\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('') + "</ul>";
	      }
	    }, options || { });
	  }
	});


