Extending jQuery UI Autocomplete

by shapeshifta

HTML

<link rel="stylesheet" href="http://static-stage.maklarpaket.se/admin/css/jquery-ui-custom.css">
<header>jizmoz<span>by mekwall</span></header>

<article>
    
    <h1 class="has-sub">Jizmoz Autocomplete</h1>
    <h3>Extending jQuery UI Autocomplete</h3>
    <br>
    
    <p>This plugin extends jQuery UI Autocomplete with the following features:</p>
    <ul>
        <li>Multiselect</li>
        <li>Auto-sized input</li>
        <li>Character trigger</li>
    </ul>
    <br>
    
    <div class="columns">
        <h2>Vanilla autoselect</h2>
        <label>Input something:</label>
        <input class="vanilla" type="text" value="" />
        <p class="hint">Begin with "A"</p>
        <br>

        <h2>Autocomplete multiselect</h2>
        <label>Input something:</label>
        <input type="text" value="" />
        <p class="hint">Begin with "A"</p>
        <br>
        
        <h2>... with @-trigger</h2>
        <label>Input something:</label>
        <input type="text" value="" />
        <p class="hint">Begin with "@"</p>
        <br>
        
    </div>

</article>
<footer></footer>

CSS

@import url(http://fonts.googleapis.com/css?family=Ubuntu:300);

body, html {
    font-family: Calibri,Verdana,Arial,sans-serif;
    border: 0;
    margin: 0;
    padding: 0;
}

h1,
h2,
h3 {
    font-family: Ubuntu;
    line-height: 1.5;
    margin-bottom: .5em;
}

h1 {
    font-size: 2em;
}

h1.has-sub {
    margin-bottom: 0;
}

h2 {
    font-size: 1.3em;
}

ul {
    list-style: disc;
    margin-left: 1.5em;
    margin-top: .5em;
    margin-bottom: .5em;
    line-height: 1.4;
}

p {
    line-height: 1.4;
    margin-bottom: .5em;
}

p.hint {
    font-style: italic;
    font-size: .8em;
    line-height: 1.5;
    color: #555;
}

p.hint:before {
    content: "Hint: "
}

header {
    background: rgb(76,76,76); /* Old browsers */
    background: -moz-linear-gradient(top, rgba(76,76,76,1) 1%, rgba(63,63,63,1) 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(1%,rgba(76,76,76,1)), color-stop(100%,rgba(63,63,63,1))); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top, rgba(76,76,76,1) 1%,rgba(63,63,63,1) 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top, rgba(76,76,76,1) 1%,rgba(63,63,63,1) 100%); /* Opera11.10+ */
    background: -ms-linear-gradient(top, rgba(76,76,76,1) 1%,rgba(63,63,63,1) 100%); /* IE10+ */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#3f3f3f',GradientType=0 ); /* IE6-9 */
    background: linear-gradient(top, rgba(76,76,76,1) 1%,rgba(63,63,63,1) 100%); /* W3C */
    padding: .2em 1.2em;
    font-family: Ubuntu;
    font-size: 1.3em;
    color: #999;
    text-shadow: -1px -1px 0 #333;
    text-align: right;
    box-shadow: 0 0 50px 20px rgba(0,0,0,.1);
    border-bottom: 1px solid #fff;
}

header span {
    margin-left: .3em;
    font-size: .6em;
}

article {
    padding: 20px;
}

label {
    vertical-align: top;
    line-height: 1.7;
}

.columns {
    padding: 2em;
    -webkit-column-count: 3;
    -webkit-column-gap:...

JavaScript

(function($, undefined) {
    if (typeof $.uix !== "object") { $.uix = {}; }
    var ac = $.ui.autocomplete.prototype;
    var _super = {
        _create: ac._create,
        _destroy: ac._destroy,
        _resizeMenu: ac._resizeMenu,
        _suggest: ac._suggest,
        search: ac.search,
        open: ac.open,
        close: ac.close
    };
    ac = $.extend({}, ac, {
        options: $.extend({}, ac.options, {
            multiselect: false,
            triggerChar: false
        }),
        _create: function(){
            var self = this,
                o = self.options;
            _super._create.apply(self);
            
            if (o.multiselect) {
                self.selectedItems = {};           
                self.multiselect = $("<div></div>")
                    .addClass("ui-autocomplete-multiselect ui-state-default ui-widget")
                    .css("width", self.element.width())
                    .insertBefore(self.element)
                    .append(self.element)
                    .bind("click.autocomplete", function(){
                        self.element.focus();
                    });
                
                var fontSize = parseInt(self.element.css("fontSize"), 10);
                function autoSize(e){
                    // Hackish autosizing
                    var $this = $(this);
                    $this.width(1).width(this.scrollWidth+fontSize-1);
                };

                var kc = $.ui.keyCode;
                self.element.bind({
                    "keydown.autocomplete": function(e){
                        if ((this.value === "") && (e.keyCode == kc.BACKSPACE)) {
                            var prev = self.element.prev();
                            delete self.selectedItems[prev.text()];
                            prev.remove();
                        }
                    },
                    // TODO: Implement outline of container
                    "focus.autocomplete blur.autocomplete":...