<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -2,31 +2,80 @@
    &quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;
 
 &lt;html lang=&quot;en&quot;&gt;
-&lt;head&gt;
-	&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot;&gt;
-	&lt;title&gt;autocomplete&lt;/title&gt;
-	&lt;meta name=&quot;generator&quot; content=&quot;TextMate http://macromates.com/&quot;&gt;
-	&lt;meta name=&quot;author&quot; content=&quot;Yehuda Katz&quot;&gt;
-	&lt;link rel=&quot;stylesheet&quot; href=&quot;jquery.ui.autocomplete.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot; /&gt;
+  &lt;head&gt;
+    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot;&gt;
+    &lt;title&gt;autocomplete&lt;/title&gt;
+    &lt;meta name=&quot;generator&quot; content=&quot;TextMate http://macromates.com/&quot;&gt;
+    &lt;meta name=&quot;author&quot; content=&quot;Yehuda Katz&quot;&gt;
+    &lt;link rel=&quot;stylesheet&quot; href=&quot;jquery.ui.autocomplete.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot; /&gt;
+    &lt;style type=&quot;text/css&quot;&gt;
+      .matching {
+      font-weight: bold;
+      text-decoration: underline;
+      }
+    &lt;/style&gt;
+
   &lt;script src=&quot;jquery.js&quot;&gt;&lt;/script&gt;
-  &lt;script src=&quot;jquery.dimensions.js&quot;&gt;&lt;/script&gt;  
+  &lt;script src=&quot;jquery.dimensions.js&quot;&gt;&lt;/script&gt;
   &lt;script src=&quot;jquery.templating.js&quot;&gt;&lt;/script&gt;
   &lt;script src=&quot;jquery.ui.autocomplete.ext.js&quot;&gt;&lt;/script&gt;
   &lt;script src=&quot;jquery.ui.autocomplete.js&quot;&gt;&lt;/script&gt;
   &lt;script&gt;
+    console = typeof(console) !== 'undefined' ? console : {};
+    console.log = typeof(console.log) !== 'undefined' ? console.log : function(msg) { $('#log').html(msg + '&lt;br/&gt;\n' + $('#log').html() ); };
+
     $(function() {
-      $(&quot;input.autocomplete&quot;).autocomplete({
-        ajax: &quot;list&quot;, 
+      $(&quot;input.autocomplete.0&quot;).autocomplete({
+        ajax: &quot;list&quot;,
         match: function(typed) { return this.text.match(new RegExp(typed)); },
         insertText: function(obj) { return obj.text },
         templateText: &quot;&lt;li&gt;Hey: &lt;%= text %&gt;&lt;/li&gt;&quot;
       })
+
+      var list1 = [{text: 'Curious George'}, {text: 'George of the Jungle'}, {text: 'Felix the Cat'}];
+      $(&quot;input.autocomplete.1&quot;).autocomplete({
+        list: list1,
+        match: function(typed) {
+          this.typed = typed;
+          this.pre_match = this.text;
+          this.match = this.post_match = '';
+          if (!this.ajax &amp;&amp; !typed || typed.length == 0) { return true; }
+          var match_at = this.text.search(new RegExp(&quot;\\b&quot; + typed, &quot;i&quot;));
+          if (match_at != -1) {
+            this.pre_match = this.text.slice(0,match_at);
+            this.match = this.text.slice(match_at,match_at + typed.length);
+            this.post_match = this.text.slice(match_at + typed.length);
+            return true;
+          }
+          return false;
+        },
+        insertText: function(obj) { return obj.text },
+        templateText: &quot;&lt;li&gt;&lt;%= pre_match %&gt;&lt;span class='matching' &gt;&lt;%= match %&gt;&lt;/span&gt;&lt;%= post_match %&gt;&lt;/li&gt;&quot;
+      });
+
+      $(&quot;input.autocomplete.2&quot;).autocomplete({
+        ajax: &quot;list&quot;,
+        match: function(typed) { return this.text.match(new RegExp(typed, &quot;i&quot;)); },
+        insertText: function(obj) { return obj.text },
+        templateText: &quot;&lt;li&gt;Hey: &lt;%= text %&gt;&lt;/li&gt;&quot;
+      })
+
+      $(&quot;input.autocomplete&quot;)
         .bind(&quot;activate.autocomplete&quot;, function(e, d) { console.log(d); })
         .bind(&quot;cancel.autocomplete&quot;, function(e) { console.log(&quot;Cancelled&quot;); });
     });
+
+
   &lt;/script&gt;
 &lt;/head&gt;
 &lt;body&gt;
-  &lt;input type=&quot;text&quot; class=&quot;autocomplete&quot; autocomplete=&quot;off&quot; /&gt;
+  &lt;input type=&quot;text&quot; class=&quot;autocomplete 0&quot; /&gt;
+
+  &lt;br /&gt;
+  &lt;form onsubmit=&quot;return false;&quot;&gt;
+    &lt;input type=&quot;text&quot; class=&quot;autocomplete 1&quot; /&gt;
+    &lt;input type=&quot;text&quot; class=&quot;autocomplete 2&quot; /&gt;
+  &lt;/form&gt;
+&lt;div id=&quot;log&quot;&gt;&lt;/div&gt;
 &lt;/body&gt;
 &lt;/html&gt;</diff>
      <filename>autocomplete.html</filename>
    </modified>
    <modified>
      <diff>@@ -11,7 +11,7 @@
  */
 
 /*
- * @description Form autocomplete plugin using preloaded or Ajax JSON data source 
+ * @description Form autocomplete plugin using preloaded or Ajax JSON data source
  *
  * @example $('input#user-name').autocomplete({list: [&quot;quentin&quot;, &quot;adam&quot;, &quot;admin&quot;]})
  * @desc Simple autocomplete with basic JSON data source
@@ -20,60 +20,75 @@
  * @desc Simple autocomplete with Ajax loaded JSON data source
  *
  */
- 
+
 
 (function($) {
-  
+
   $.ui = $.ui || {}; $.ui.autocomplete = $.ui.autocomplete || {}; var active;
-    
+
+  var KEY = {
+    ESC: 27,
+    RETURN: 13,
+    TAB: 9,
+    BS: 8,
+    DEL: 46,
+    UP: 38,
+    DOWN: 40
+  };
+
   $.fn.autocompleteMode = function(container, input, size, opt) {
     var original = input.val(); var selected = -1; var self = this;
-    
+
     $.data(document.body, &quot;autocompleteMode&quot;, true);
 
-    $(&quot;body&quot;).one(&quot;cancel.autocomplete&quot;, function() { 
-      input.trigger(&quot;cancel.autocomplete&quot;); $(&quot;body&quot;).trigger(&quot;off.autocomplete&quot;); input.val(original); 
+    $(&quot;body&quot;).one(&quot;cancel.autocomplete&quot;, function() {
+      input.trigger(&quot;cancel.autocomplete&quot;); $(&quot;body&quot;).trigger(&quot;off.autocomplete&quot;); input.val(original);
     });
-    
+
     $(&quot;body&quot;).one(&quot;activate.autocomplete&quot;, function() {
-      input.trigger(&quot;activate.autocomplete&quot;, [$.data(active[0], &quot;originalObject&quot;)]); $(&quot;body&quot;).trigger(&quot;off.autocomplete&quot;);
+      // Try hitting return to activate autocomplete and then hitting it again on blank input
+      // to close it.  w/o checking the active object first this input.trigger() will barf.
+      active &amp;&amp; input.trigger(&quot;activate.autocomplete&quot;, [$.data(active[0], &quot;originalObject&quot;)]);
+      $(&quot;body&quot;).trigger(&quot;off.autocomplete&quot;);
     });
-    
+
     $(&quot;body&quot;).one(&quot;off.autocomplete&quot;, function(e, reset) {
       container.remove();
       $.data(document.body, &quot;autocompleteMode&quot;, false);
       input.unbind(&quot;keydown.autocomplete&quot;);
       $(&quot;body&quot;).add(window).unbind(&quot;click.autocomplete&quot;).unbind(&quot;cancel.autocomplete&quot;).unbind(&quot;activate.autocomplete&quot;);
     });
-    
+
     // If a click bubbles all the way up to the window, close the autocomplete
     $(window).bind(&quot;click.autocomplete&quot;, function() { $(&quot;body&quot;).trigger(&quot;cancel.autocomplete&quot;); });
 
     var select = function() {
       active = $(&quot;&gt; *&quot;, container).removeClass(&quot;active&quot;).slice(selected, selected + 1).addClass(&quot;active&quot;);
-      input.trigger(&quot;itemSelected.autocomplete&quot;, [$.data(active[0], &quot;originalObject&quot;)]);     
+      input.trigger(&quot;itemSelected.autocomplete&quot;, [$.data(active[0], &quot;originalObject&quot;)]);
       input.val(opt.insertText($.data(active[0], &quot;originalObject&quot;)));
     };
-    
+
     container.mouseover(function(e) {
       // If you hover over the container, but not its children, return
       if(e.target == container[0]) return;
       // Set the selected item to the item hovered over and make it active
       selected = $(&quot;&gt; *&quot;, container).index($(e.target).is('li') ? $(e.target)[0] : $(e.target).parents('li')[0]); select();
     }).bind(&quot;click.autocomplete&quot;, function(e) {
-      $(&quot;body&quot;).trigger(&quot;activate.autocomplete&quot;); $.data(document.body, &quot;suppressKey&quot;, false); 
+      $(&quot;body&quot;).trigger(&quot;activate.autocomplete&quot;); $.data(document.body, &quot;suppressKey&quot;, false);
     });
-    
+
     input
       .bind(&quot;keydown.autocomplete&quot;, function(e) {
-        if(e.which == 27) { $(&quot;body&quot;).trigger(&quot;cancel.autocomplete&quot;); }
-        else if(e.which == 13) { $(&quot;body&quot;).trigger(&quot;activate.autocomplete&quot;); }
-        else if(e.which == 40 || e.which == 9 || e.which == 38) {
-          switch(e.which) {
-            case 40: 
-            case 9:
+        var k = e.which || e.keyCode; // in IE e.which is undefined
+
+        if(k == KEY.ESC) { $(&quot;body&quot;).trigger(&quot;cancel.autocomplete&quot;); }
+        else if(k == KEY.RETURN) { $(&quot;body&quot;).trigger(&quot;activate.autocomplete&quot;); }
+        else if(k == KEY.UP || k == KEY.TAB || k == KEY.DOWN) {
+          switch(k) {
+            case KEY.DOWN:
+            case KEY.TAB:
               selected = selected &gt;= size - 1 ? 0 : selected + 1; break;
-            case 38:
+            case KEY.UP:
               selected = selected &lt;= 0 ? size - 1 : selected - 1; break;
             default: break;
           }
@@ -82,9 +97,9 @@
         $.data(document.body, &quot;suppressKey&quot;, true);
       });
   };
-  
+
   $.fn.autocomplete = function(opt) {
-    
+
     opt = $.extend({}, {
       timeout: 1000,
       getList: function(input) { input.trigger(&quot;updateList&quot;, [opt.list]); },
@@ -103,18 +118,19 @@
     } }
 
     return this.each(function() {
-  
+
       $(this)
         .keypress(function(e) {
           var typingTimeout = $.data(this, &quot;typingTimeout&quot;);
+          var k = e.keyCode || e.which; // keyCode == 0 in Gecko/FF on keypress
           if(typingTimeout) window.clearInterval(typingTimeout);
-                    
+
           if($.data(document.body, &quot;suppressKey&quot;))
             return $.data(document.body, &quot;suppressKey&quot;, false);
-          else if($.data(document.body, &quot;autocompleteMode&quot;) &amp;&amp; e.charCode &lt; 32 &amp;&amp; e.keyCode != 8 &amp;&amp; e.keyCode != 46) return false;          
-          else {
-            $.data(this, &quot;typingTimeout&quot;, window.setTimeout(function() { 
-              $(e.target).trigger(&quot;autocomplete&quot;); 
+          else if($.data(document.body, &quot;autocompleteMode&quot;) &amp;&amp; k &lt; 32 &amp;&amp; k != KEY.BS &amp;&amp; k != KEY.DEL) return false;
+          else if (k != KEY.TAB  &amp;&amp; k != KEY.ESC) { // don't start the meter running if we are tabbing out or escaping this nuthouse
+            $.data(this, &quot;typingTimeout&quot;, window.setTimeout(function() {
+               $(e.target).trigger(&quot;autocomplete&quot;);
             }, opt.timeout));
           }
         })
@@ -127,21 +143,21 @@
               .map(function() {
                 var node = $(opt.template(this))[0];
                 $.data(node, &quot;originalObject&quot;, this);
-                return node; 
+                return node;
               });
-          
+
             $(&quot;body&quot;).trigger(&quot;off.autocomplete&quot;);
-          
+
             if(!list.length) return false;
-          
+
             var container = list.wrapAll(opt.wrapper).parents(&quot;:last&quot;).children();
-            
+
             var offset = self.offset();
-          
+
             opt.container = container
               .css({top: offset.top + self.outerHeight(), left: offset.left, width: self.width()})
               .appendTo(&quot;body&quot;);
-          
+
             $(&quot;body&quot;).autocompleteMode(container, self, list.length, opt);
           });
 
@@ -150,5 +166,5 @@
 
     });
   };
-  
+
 })(jQuery);</diff>
      <filename>jquery.ui.autocomplete.js</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>1dbbaa3198a9fae6726ec2c0f2115a54fa60beea</id>
    </parent>
  </parents>
  <author>
    <name>Ian Struble</name>
    <email>istruble@gmail.com</email>
  </author>
  <url>http://github.com/istruble/jquery-autocomplete/commit/723d7ac6ffdbee6839f47ec8ed5823e385059909</url>
  <id>723d7ac6ffdbee6839f47ec8ed5823e385059909</id>
  <committed-date>2008-04-12T12:51:00-07:00</committed-date>
  <authored-date>2008-04-12T12:46:51-07:00</authored-date>
  <message>Safari-mac TAB behavior cleaned up and fixed RET-RET bug.

Tabbing in Safari-mac to another field was
  - moving the selection (good)
  - updating the input field (good)
  - moving focus to the next tab w/o ending the autocomplete (bad)
Now only cycles selection on TAB if active and lets the browser do
what it wants if not active.

Hitting RET-RET on an empty input field was:
  - activating autocomplete on RET (good)
  - throwing an error and remaining active on 2nd RET (bad)
Now it just closes on the second RET press.

Also changed the key calculation for keydown or keypressed events
to match browser key input quirks based on info at
  http://unixpapa.com/js/key.html</message>
  <tree>c96a148cea2c008204bda55535e5c3b4e678b59d</tree>
  <committer>
    <name>Ian Struble</name>
    <email>istruble@gmail.com</email>
  </committer>
</commit>
