Object.extend(Autocompleter.Base.prototype, {
    // Adding instructions to "baseInitialize".
    baseInitialize: Autocompleter.Base.prototype.baseInitialize.wrap(function(legacy) {
        legacy.apply(this, $A(arguments).without(legacy));

        // PATCH: cf. patch for "onBlur". When the user has clicked on the scroll bar, the field loses its focus.
        // So, "onBlur" is not triggered anymore when he clicks outside of the suggestion list. The following listener
        // corrects that by detecting the "blur" events triggered for the suggestion list "div".
        Event.observe(this.update, "blur", function(event) {
            if(this._IE_scrolling) {
                this._IE_scrolling = false;
                this.onBlur(event);
            }
        }.bind(this));
    }),

    // Changes:
    //   - using «scrollIntoView» was making the window scrolling brutally in addition of the list scrolling.
    markPrevious: function() {
        this.index = (this.index > 0) ? this.index - 1 : 0;
        var entry = this.getEntry(this.index);
        var layer = entry.parentNode.parentNode;
        var position = entry.positionedOffset();
        var overlap = {
            top: layer.scrollTop - position[1],
            bottom: (position[1] + entry.getHeight()) - (layer.getHeight() + layer.scrollTop)
        }
        var threshold = (0.1 * entry.getHeight());
        if(overlap.top > threshold) {
            layer.scrollTop = position[1];
        }
        else if(overlap.bottom > threshold) {
            layer.scrollTop += overlap.bottom;
        }
    },

    // Changes:
    //   - using «scrollIntoView» was making the window scrolling brutally in addition of the list scrolling.
    markNext: function() {
        this.index = (this.index < this.entryCount - 1) ? this.index + 1 : this.entryCount - 1;
        var entry = this.getEntry(this.index);
        var layer = entry.parentNode.parentNode;
        var position = entry.positionedOffset();
        var overlap = {
                top: layer.scrollTop - position[1],
                bottom: (position[1] + entry.getHeight()) - (layer.getHeight() + layer.scrollTop)
        };
        var threshold = (0.1 * entry.getHeight());
        if(overlap.top > threshold) {
            layer.scrollTop = position[1];
        }
        else if(overlap.bottom > threshold) {
            layer.scrollTop += overlap.bottom;
        }
    },

    // Changes:
    //   - removed «this.element.focus();» after entry selection.
    updateElement: function(selectedElement) {
        if(this.options.updateElement) {
            this.options.updateElement(selectedElement);
            return;
        }
        var value = "";
        if(this.options.select) {
            var nodes = $(selectedElement).select("." + this.options.select) || [];
            if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
        } else
            value = Element.collectTextNodesIgnoreClass(selectedElement, "informal");

        var bounds = this.getTokenBounds();
        if(bounds[0] != -1) {
            var newValue = this.element.value.substr(0, bounds[0]);
            var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
            if(whitespace) { newValue += whitespace[0]; }
            this.element.value = newValue + value + this.element.value.substr(bounds[1]);
        }
        else {
            this.element.value = value;
        }
        this.oldElementValue = this.element.value;

        if(this.options.afterUpdateElement) {
            this.options.afterUpdateElement(this.element, selectedElement);
        }
    },

    onBlur: function(event) {
        // PATCH: clicking on the suggestion list scroll-bar in IE makes the suggestion list disappear...
        // With "surrounds" we check if the coordinates of the "blur" event (IE-specific) are within the suggestion list
        // bounds.
		var viewport = document.viewport.getScrollOffsets();
        if(!Prototype.Browser.IE
           || !this.update.surrounds((event.x || 999999), (event.y || 999999), {
        			context: document,
        			margin: {
        				top: viewport.top,
        				bottom: viewport.top
        			}
    			})) {
            // needed to make click events working
            setTimeout(this.hide.bind(this), 250);
            this.hasFocus = false;
            this.active = false;
        }
        else {
            this._IE_scrolling = true;
        }
    }
});

Object.extend(Autocompleter.Local.prototype, {
    // Changes:
    //   - specific style for the highlighted parts of the suggestions (not only surrounding with a "b" tag anymore).
    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);
                    // Specific style for the highlighted parts of the suggestions.
                    while(foundPos != -1) {
                        if((foundPos == 0) && (elem.length != entry.length)) {
                            ret.push("<li><span class='match'>" + elem.substr(0, entry.length) + "</span>"
                                + 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) + "<span class='match'>"
                                        + elem.substr(foundPos, entry.length) + "</span>"
                                        + 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 || {});
    }
});