Index: htdocs/css/reviews.css
===================================================================
--- htdocs/css/reviews.css	(revision 973)
+++ htdocs/css/reviews.css	(working copy)
@@ -415,6 +415,164 @@
   visibility: hidden;
 }
 
+/* yui-autocomplete skin */
+
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.3.1
+*/
+
+/* styles for entire widget */
+.yui-skin-sam .yui-ac
+{
+    position: relative;
+    font-family: arial;
+    font-size: 100%;
+}
+/* styles for input field */
+.yui-skin-sam .yui-ac-input
+{
+    position: absolute;
+    width: 100%;
+}
+/* styles for results container */
+.yui-skin-sam .yui-ac-container
+{
+    position: absolute;
+    top: 1.6em;
+    width: 100%;
+}
+/* styles for header/body/footer wrapper within container */
+.yui-skin-sam .yui-ac-content
+{
+    position: absolute;
+    width: 100%;
+    border: 1px solid #808080;
+    background: #fff;
+    overflow: hidden;
+    z-index: 9050;
+}
+/* styles for container shadow */
+.yui-skin-sam .yui-ac-shadow
+{
+    position: absolute;
+    margin: .3em;
+    width: 100%;
+    background: #000;
+    -moz-opacity: 0.10;
+    opacity: .10;
+    filter: alpha(opacity=10);
+    z-index: 9049;
+}
+/* styles for results list */
+.yui-skin-sam .yui-ac-content ul
+{
+    margin: 0;
+    padding: 0;
+    width: 100%;
+}
+/* styles for result item */
+.yui-skin-sam .yui-ac-content li
+{
+    margin: 0;
+    padding: 2px 5px;
+    cursor: default;
+    white-space: nowrap;
+}
+/* styles for prehighlighted result item */
+.yui-skin-sam .yui-ac-content li.yui-ac-prehighlight
+{
+    background: #B3D4FF;
+}
+/* styles for highlighted result item */
+.yui-skin-sam .yui-ac-content li.yui-ac-highlight
+{
+    background: #426FD9;
+    color: #FFF;
+}
+/* styles for this implementation */
+#ysearchcontainer
+{
+    width: 15em; /* set width for widget here */
+    padding-bottom: 2em;
+}
+.yui-skin-sam .yui-ac
+{
+    position:relative;
+    font-family:arial;
+    font-size:100%;
+}
+.yui-skin-sam .yui-ac-input
+{
+    position:absolute;
+    width:100%;
+}
+.yui-skin-sam .yui-ac-container
+{
+    position:absolute;
+    top:1.6em;
+    width:100%;
+}
+.yui-skin-sam .yui-ac-content
+{
+    position:absolute;
+    width:100%;
+    border:1px solid #808080;
+    background:#fff;
+    overflow:hidden;
+    z-index:9050;
+}
+.yui-skin-sam .yui-ac-shadow
+{
+    position: absolute;
+    margin: .3em;
+    width: 100%;
+    background: #000;
+    -moz-opacity: 0.10;
+    opacity: .10;
+    filter: alpha(opacity=10);
+    z-index: 9049;
+}
+.yui-skin-sam .yui-ac-content ul
+{
+    margin: 0;
+    padding: 0;
+    width: 100%;
+}
+.yui-skin-sam .yui-ac-content li
+{
+    margin: 0;
+    padding: 2px 5px;
+    cursor: default;
+    white-space: nowrap;
+}
+.yui-skin-sam .yui-ac-content li.yui-ac-prehighlight
+{
+    background: #B3D4FF;
+}
+.yui-skin-sam .yui-ac-content li.yui-ac-highlight
+{
+    background: #426FD9;
+    color: #FFF;
+}
+
+/* custom styles for centered example */
+#ysearch {
+    text-align: center;
+}
+ /* to center, set static and explicit width: */
+#ysearchinput {
+    position: static;
+    width: 20em;
+}
+ /* to center, set left-align and explicit width: */
+#ysearchcontainer {
+    text-align: left;
+    width: 20em;
+}
+
 /* !(*%!(&^ (see http://www.positioniseverything.net/easyclearing.html) */
 .clearfix:after {
   clear: both;
Index: htdocs/scripts/rb/widgets.js
===================================================================
--- htdocs/scripts/rb/widgets.js	(revision 973)
+++ htdocs/scripts/rb/widgets.js	(working copy)
@@ -52,13 +52,61 @@
         this.saveButton = block.createChild(saveButton);
         this.cancelButton = block.createChild(cancelButton);
     } else {
-        this.field = this.form.createChild({
-            tag: 'input',
-            type: 'text'
-        });
+        if (this.autocomplete) {
+            var auto_id = 'autoComplete_' + this.cls;
+            var autoinput_id = 'autoInput_' + this.cls;
+            var container_id = "ysearchcontainer_" + this.cls;
 
-        this.saveButton = this.form.createChild(saveButton);
-        this.cancelButton = this.form.createChild(cancelButton);
+            this.skin = this.form.createChild({
+                tag: 'div',
+                cls: 'yui-skin-sam'
+            });
+
+            this.autodiv = this.skin.createChild({
+                tag: 'div',
+                id: auto_id,
+                cls: 'yui-ac'
+            });
+
+            this.field = this.autodiv.createChild({
+                tag:    'input',
+                type:   'text',
+                id:     autoinput_id,
+                cls:  'yui-ac-input'
+            });
+
+            this.autodiv.createChild({
+                tag:    'div',
+                id:     container_id,
+                cls:  'yui-ac-container'
+            });
+
+            this.left = this.skin.createChild({
+                tag:    'div',
+                style:  'float: right;'
+            });
+            this.saveButton = this.left.createChild(saveButton);
+            this.cancelButton = this.left.createChild(cancelButton);
+
+            var myAutoComp = new YAHOO.widget.AutoComplete(autoinput_id,
+                                                           container_id,
+                                                           this.autocomplete);
+            myAutoComp.animVert = true;
+            myAutoComp.animHoriz = false;
+            myAutoComp.typeAhead = true;
+            myAutoComp.allowBrowserAutocomplete = false;
+            myAutoComp.prehighlightClassName = "yui-ac-prehighlight";
+            myAutoComp.delimChar = [",", " "];
+            myAutoComp.minQueryLength = 1;
+            myAutoComp.queryDelay = 0;
+        } else {
+            this.field = this.form.createChild({
+                tag: 'input',
+                type: 'text'
+            });
+            this.saveButton = this.form.createChild(saveButton);
+            this.cancelButton = this.form.createChild(cancelButton);
+        }
     }
 
     this.saveButton.enableDisplayMode();
Index: htdocs/scripts/reviews.js
===================================================================
--- htdocs/scripts/reviews.js	(revision 973)
+++ htdocs/scripts/reviews.js	(working copy)
@@ -58,6 +58,7 @@
 	var editor = new RB.widgets.InlineCommaListEditor({
 		el: field,
 		cls: field + '-editor',
+                autocomplete: false,
 		showEditIcon: true,
 		useEditIconOnly: true,
 		notifyUnchangedCompletion: true
@@ -68,6 +69,22 @@
 	});
 }
 
+function registerAutoCompleteCommaListEditor(field, onComplete, url, columns) {
+	var myDataSource = new YAHOO.widget.DS_XHR(url, columns);
+	var editor = new RB.widgets.InlineCommaListEditor({
+		el: field,
+		cls: field + '-editor',
+		autocomplete: myDataSource,
+		showEditIcon: true,
+		useEditIconOnly: true,
+		notifyUnchangedCompletion: true
+	});
+
+	editor.on('complete', function(editor, value) {
+		onEditComplete(field, value, onComplete);
+	});
+}
+
 function onBugsChanged(el, list) {
 	var str = "";
 
Index: reviews/json.py
===================================================================
--- reviews/json.py	(revision 973)
+++ reviews/json.py	(working copy)
@@ -325,8 +325,34 @@
         'repositories': Repository.objects.all(),
     })
 
+@json_login_required
+def user_list(request):
+    query = request.GET.get('query', None)
+    columns = ['username', 'first_name', 'last_name']
+    if not query:
+        u = User.objects.filter(is_active__exact=True).values(*columns)
+    else:
+        u = User.objects.filter(is_active__exact=True,
+                                username__startswith=query).values(*columns)
 
+    return JsonResponse(request, {
+        'users': u,
+    })
+
 @json_login_required
+def group_list(request):
+    query = request.GET.get('query', None)
+    columns = ['display_name', 'name']
+    if not query:
+        u = Group.objects.values(*columns)
+    else:
+        u = Group.objects.filter(name__startswith=query).values(*columns)
+
+    return JsonResponse(request, {
+        'groups': u,
+    })
+
+@json_login_required
 def group_star(request, group_name):
     try:
         group = Group.objects.get(name=group_name)
Index: reviews/urls/json.py
===================================================================
--- reviews/urls/json.py	(revision 973)
+++ reviews/urls/json.py	(working copy)
@@ -26,6 +26,12 @@
     # Repositories
     (r'^repositories/$', 'repository_list'),
 
+    # Users
+    (r'^users/$', 'user_list'),
+
+    # Groups
+    (r'^groups/$', 'group_list'),
+
     # Review groups
     (r'^groups/(?P<group_name>[A-Za-z0-9_-]+)/star/$',
      'group_star'),
Index: templates/reviews/review_detail.html
===================================================================
--- templates/reviews/review_detail.html	(revision 973)
+++ templates/reviews/review_detail.html	(working copy)
@@ -13,6 +13,7 @@
 <script type="text/javascript" src="/scripts/yui/yahoo/yahoo-min.js"></script>
 <script type="text/javascript" src="/scripts/yui/animation/animation-min.js"></script>
 <script type="text/javascript" src="/scripts/yui/connection/connection-min.js"></script>
+<script type="text/javascript" src="/scripts/yui/autocomplete/autocomplete-min.js"></script>
 <script type="text/javascript" src="/scripts/yui/event/event-min.js"></script>
 <script type="text/javascript" src="/scripts/yui/dom/dom-min.js"></script>
 <script type="text/javascript" src="/scripts/yui/utilities/utilities.js"></script>
@@ -153,10 +154,10 @@
       <tbody class="{{chunk.change}}">
 {% for line in chunk.lines %}
        <tr{% ifnotequal chunk.change "equal" %}{% attr "class" %}{% if forloop.first %}first{% endif %} {% if forloop.last %}last{% endif %}{% endattr %}{% endifnotequal %}>
-        <th>{{line.1}}</td>
-        <td><pre>{{ line.2|highlightregion:line.3|showextrawhitespace }}</pre></td>
-        <th>{{line.4}}</td>
-        <td><pre>{{ line.5|highlightregion:line.6|showextrawhitespace }}</pre></td>
+         <th>{{line.1}}</td>
+         <td><pre>{{ line.2|highlightregion:line.3|showextrawhitespace }}</pre></td>
+         <th>{{line.4}}</td>
+         <td><pre>{{ line.5|highlightregion:line.6|showextrawhitespace }}</pre></td>
        </tr>
 {% endfor %}
       </tbody>
@@ -186,8 +187,14 @@
   registerEditor('description', true);
   registerEditor('testing_done', true);
   registerCommaListEditor('bugs_closed', onBugsChanged);
-  registerCommaListEditor('target_groups', onTargetGroupsChanged);
-  registerCommaListEditor('target_people', onTargetPeopleChanged);
+  registerAutoCompleteCommaListEditor('target_groups',
+                                        onTargetGroupsChanged,
+                                        "/api/json/groups/",
+                                        ["groups", "name"]);
+  registerAutoCompleteCommaListEditor('target_people',
+                                        onTargetPeopleChanged,
+                                        "/api/json/users/",
+                                        ["users", "username"]);
 
 {% for image in review_request_details.screenshots.all %}
   registerEditor('screenshot_{{image.id}}_caption', false);
