Browse Source

HUE-803 The useradmin module needs some UI enhancements

Changed filter bar to look and behave like Filebrowser
Improved jHueSelector search results look, added height parameter, improved behavior for empty lists
Enrico Berti 13 years ago
parent
commit
bb06548f4d

+ 10 - 4
apps/useradmin/src/useradmin/templates/edit_group.mako

@@ -53,6 +53,8 @@ ${layout.menubar(section='groups', _=_)}
 		% endif
 	% endif
 
+    <br/>
+
 	<form id="editForm" action="${urllib.quote(action)}" method="POST" class="form form-horizontal">
 		<fieldset>
 		% for field in form:
@@ -78,14 +80,18 @@ ${layout.menubar(section='groups', _=_)}
 <script type="text/javascript" charset="utf-8">
 	$(document).ready(function(){
 		$("#id_members").jHueSelector({
-            width:400,
             selectAllLabel: "${_('Select all')}",
-            searchPlaceholder: "${_('Search')}"
+            searchPlaceholder: "${_('Search')}",
+            noChoicesFound: "${_('No users found.')}",
+            width:600,
+            height:240
         });
 		$("#id_permissions").jHueSelector({
-            width:400,
             selectAllLabel: "${_('Select all')}",
-            searchPlaceholder: "${_('Search')}"
+            searchPlaceholder: "${_('Search')}",
+            noChoicesFound: "${_('No permissions found.')}",
+            width:600,
+            height:240
         });
 	});
 </script>

+ 4 - 2
apps/useradmin/src/useradmin/templates/edit_permissions.mako

@@ -58,9 +58,11 @@ ${layout.menubar(section='permissions', _=_)}
 <script type="text/javascript" charset="utf-8">
 	$(document).ready(function(){
 		$("#id_groups").jHueSelector({
-            width:520,
             selectAllLabel: "${_('Select all')}",
-            searchPlaceholder: "${_('Search')}"
+            searchPlaceholder: "${_('Search')}",
+            noChoicesFound: "${_('No groups found.')}",
+            width:600,
+            height:240
         });
 	});
 </script>

+ 29 - 2
apps/useradmin/src/useradmin/templates/edit_user.mako

@@ -50,10 +50,34 @@ ${layout.menubar(section='users', _=_)}
 		% endif
 	% endif
 
+    <br/>
+
 	<form id="editForm" action="${urllib.quote(action)}" method="POST" class="form form-horizontal">
 		<fieldset>
 			% for field in form:
-				${render_field(field)}
+                %if field.name == "first_name":
+                    <div class="row">
+                        <div class="span5">
+                        ${render_field(form["first_name"])}
+                        </div>
+                        <div class="span4">
+                        ${render_field(form["last_name"])}
+                        </div>
+                    </div>
+                %elif field.name == "password1":
+                    <div class="row">
+                        <div class="span5">
+                        ${render_field(form["password1"])}
+                        </div>
+                        <div class="span4">
+                        ${render_field(form["password2"])}
+                        </div>
+                    </div>
+                %elif field.name == "last_name" or field.name == "password2":
+                    ## skip rendering
+                %else:
+				    ${render_field(field)}
+                %endif
 			% endfor
 		</fieldset>
 		<br/>
@@ -75,7 +99,10 @@ ${layout.menubar(section='users', _=_)}
 	$(document).ready(function(){
 		$("#id_groups").jHueSelector({
             selectAllLabel: "${_('Select all')}",
-            searchPlaceholder: "${_('Search')}"
+            searchPlaceholder: "${_('Search')}",
+            noChoicesFound: "${_('No groups found.')}",
+            width:618,
+            height:240
         });
 	});
 </script>

+ 42 - 0
apps/useradmin/src/useradmin/templates/layout.mako

@@ -37,3 +37,45 @@ def is_selected(section, matcher):
 	</div>
 </%def>
 
+<%def name="commons()">
+    <style type="text/css">
+        .fixed {
+            position: fixed;
+            top: 80px;
+            filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
+            -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+            -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+            box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+        }
+        .pull-right {
+            margin: 4px;
+        }
+        .sortable {
+            cursor: pointer;
+        }
+        .file-row {
+            height:37px;
+        }
+    </style>
+    <script type="text/javascript">
+        $(document).ready(function(){
+            $("#filterInput").keyup(function(){
+                var shown = 0;
+                $(".datatables tfoot").hide();
+                $.each($(".tableRow"), function(index, value) {
+                    if($(value).data("search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
+                        $(value).hide();
+                    }
+                    else{
+                        $(value).show();
+                        shown++;
+                    }
+                });
+                if (shown == 0){
+                    $(".datatables tfoot").show();
+                }
+            });
+        });
+    </script>
+</%def>
+

+ 63 - 34
apps/useradmin/src/useradmin/templates/list_groups.mako

@@ -26,43 +26,63 @@ ${layout.menubar(section='groups', _=_)}
 
 <div class="container-fluid">
     <h1>${_('Hue Groups')}</h1>
-    <div class="well hueWell">
-        <div class="pull-right btn-group">
-            %if user.is_superuser == True:
-            <a id="addGroupBtn" href="${url('useradmin.views.edit_group')}" class="btn">${_('Add group')}</a>
-            <a id="addLdapGroupBtn" href="${url('useradmin.views.add_ldap_group')}" class="btn">${_('Add/Sync LDAP group')}</a>
-            %endif
+    <div class="subnavContainer">
+        <div class="subnav sticky">
+            <p class="pull-right">
+                <input id="filterInput" type="text" class="input-xlarge search-query" placeholder="${_('Search for group name, members, etc...')}">
+            </p>
+            <p style="padding: 4px">
+                %if user.is_superuser:
+                    <button id="deleteGroupBtn" class="btn confirmationModal" title="${_('Delete')}" disabled="disabled"><i class="icon-trash"></i> ${_('Delete')}</button>
+                    &nbsp;&nbsp;&nbsp;&nbsp;
+                    <a id="addGroupBtn" href="${url('useradmin.views.edit_group')}" class="btn"><i class="icon-plus-sign"></i> ${_('Add group')}</a>
+                    <a id="addLdapGroupBtn" href="${url('useradmin.views.add_ldap_group')}" class="btn"><i class="icon-refresh"></i> ${_('Add/Sync LDAP group')}</a>
+                    &nbsp;&nbsp;&nbsp;&nbsp;
+                %endif
+            </p>
         </div>
-        <form class="form-search">
-            ${_('Filter: ')}<input type="text" id="filterInput" class="input-xxlarge search-query" placeholder="${_('Search for group name, members, etc...')}">
-        </form>
     </div>
+    <br/>
     <table class="table table-striped table-condensed datatables">
     <thead>
       <tr>
+        %if user.is_superuser:
+          <th width="1%"><input id="selectAll" type="checkbox"/></th>
+        %endif
         <th>${_('Group Name')}</th>
         <th>${_('Members')}</th>
         <th>${_('Permissions')}</th>
-        %if user.is_superuser == True:
-        <th>&nbsp;</th>
-        %endif
       </tr>
     </thead>
     <tbody>
     % for group in groups:
-      <tr class="groupRow" data-search="${group.name}${', '.join([group_user.username for group_user in group.user_set.all()])}">
-        <td>${group.name}</td>
+      <tr class="tableRow" data-search="${group.name}${', '.join([group_user.username for group_user in group.user_set.all()])}">
+        %if user.is_superuser:
+          <td class="center" data-row-selector-exclude="true">
+            <input type="checkbox" class="groupCheck" data-group="${group.name}" data-confirmation-url="${ url('useradmin.views.delete_group', name=urllib.quote_plus(group.name)) }" data-row-selector-exclude="true"/>
+          </td>
+        %endif
+        <td>
+            %if user.is_superuser:
+            <h5><a title="${_('Edit %(groupname)s') % dict(groupname=group.name)}" href="${ url('useradmin.views.edit_group', name=urllib.quote(group.name)) }" data-row-selector="true">${group.name}</a></h5>
+            %else:
+            <h5>${group.name}</h5>
+            %endif
+         </td>
         <td>${', '.join([group_user.username for group_user in group.user_set.all()])}</td>
         <td>${', '.join([perm.app + "." + perm.action for perm in group_permissions(group)])}</td>
-        %if user.is_superuser == True:
-        <td class="right">
-          <a title="${_('Edit %(groupname)s') % dict(groupname=group.name)}" class="btn small editGroupBtn" href="${ url('useradmin.views.edit_group', name=urllib.quote(group.name)) }" data-row-selector="true">${_('Edit')}</a>
-          <a title="${_('Delete %(groupname)s') % dict(groupname=group.name)}" class="btn small confirmationModal" alt="${ _('Are you sure you want to delete %(group_name)s?') % dict(group_name=group.name) }" href="javascript:void(0)" data-confirmation-url="${ url('useradmin.views.delete_group', name=urllib.quote_plus(group.name)) }">${_('Delete')}</a>
-        </td>
-        %endif
       </tr>
     % endfor
     </tbody>
+    <tfoot class="hide">
+      <tr>
+        <td colspan="8">
+          <div class="alert">
+            ${_('There are no groups matching the search criteria.')}
+          </div>
+        </td>
+      </tr>
+    </tfoot>
     </table>
 </div>
 
@@ -80,12 +100,12 @@ ${layout.menubar(section='groups', _=_)}
                 "bFilter": false,
                 "bAutoWidth": false,
                 "aoColumns": [
+                    %if user.is_superuser:
+                    { "bSortable": false },
+                    %endif
                     { "sWidth": "20%" },
                     { "sWidth": "20%" },
-                    null,
-                    %if user.is_superuser == True:
-                    { "sWidth": "140px", "bSortable": false },
-                    %endif
+                    null
                  ]
             });
             $(".dataTables_wrapper").css("min-height","0");
@@ -94,7 +114,7 @@ ${layout.menubar(section='groups', _=_)}
             $(".confirmationModal").click(function(){
                 var _this = $(this);
                 $.ajax({
-                    url: _this.attr("data-confirmation-url"),
+                    url: _this.data("confirmation-url"),
                     beforeSend: function(xhr){
                         xhr.setRequestHeader("X-Requested-With", "Hue");
                     },
@@ -106,20 +126,29 @@ ${layout.menubar(section='groups', _=_)}
                 });
             });
 
-            $("#filterInput").keyup(function(){
-                $.each($(".groupRow"), function(index, value) {
-
-                  if($(value).data("search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
-                    $(value).hide(250);
-                  }else{
-                    $(value).show(250);
-                  }
-                });
+            $("#selectAll").change(function(){
+                if ($(this).is(":checked")){
+                    $(".groupCheck").attr("checked", "checked");
+                }
+                else {
+                    $(".groupCheck").removeAttr("checked");
+                }
+                $(".groupCheck").change();
+            });
 
+            $(".groupCheck").change(function(){
+                if ($(".groupCheck:checked").length == 1){
+                    $("#deleteGroupBtn").removeAttr("disabled").data("confirmation-url", $(".groupCheck:checked").data("confirmation-url"));
+                }
+                else {
+                    $("#deleteGroupBtn").attr("disabled", "disabled");
+                }
             });
 
             $("a[data-row-selector='true']").jHueRowSelector();
         });
     </script>
 
+${layout.commons()}
+
 ${commonfooter(messages)}

+ 27 - 30
apps/useradmin/src/useradmin/templates/list_permissions.mako

@@ -27,36 +27,46 @@ ${layout.menubar(section='permissions', _=_)}
 
 <div class="container-fluid">
     <h1>${_('Hue Permissions')}</h1>
-    <div class="well hueWell">
-        <form class="form-search">
-                ${_('Filter: ')}<input type="text" id="filterInput" class="input-xxlarge search-query" placeholder="${_('Search for application name, description, etc...')}">
-        </form>
+    <div class="subnavContainer">
+        <div class="subnav sticky">
+            <p class="pull-right">
+                <input id="filterInput" type="text" class="input-xlarge search-query" placeholder="${_('Search for application name, description, etc...')}">
+            </p>
+        </div>
     </div>
+    <br/>
     <table class="table table-striped table-condensed datatables">
         <thead>
         <tr>
             <th>${_('Application')}</th>
             <th>${_('Permission')}</th>
             <th>${_('Groups')}</th>
-            %if user.is_superuser:
-                <th>&nbsp;</th>
-            %endif
         </tr>
         </thead>
         <tbody>
             % for perm in permissions:
-            <tr class="permissionRow" data-search="${perm.app}${perm.description}${', '.join([group.name for group in Group.objects.filter(grouppermission__hue_permission=perm).order_by('name')])}">
-                <td>${perm.app}</td>
+            <tr class="tableRow" data-search="${perm.app}${perm.description}${', '.join([group.name for group in Group.objects.filter(grouppermission__hue_permission=perm).order_by('name')])}">
+                <td>
+                %if user.is_superuser:
+                    <h5><a title="${_('Edit permission')}" href="${ url('useradmin.views.edit_permission', app=urllib.quote(perm.app), priv=urllib.quote(perm.action)) }" data-name="${perm.app}" data-row-selector="true">${perm.app}</a></h5>
+                %else:
+                    <h5>${perm.app}</h5>
+                %endif
+                </td>
                 <td>${perm.description}</td>
                 <td>${', '.join([group.name for group in Group.objects.filter(grouppermission__hue_permission=perm).order_by('name')])}</td>
-            %if user.is_superuser:
-                <td class="right">
-                    <a title="${_('Edit permission')}" class="btn small editPermissionBtn" href="${ url('useradmin.views.edit_permission', app=urllib.quote(perm.app), priv=urllib.quote(perm.action)) }" data-name="${perm.app}" data-row-selector="true">${_('Edit')}</a>
-                </td>
-            %endif
             </tr>
             % endfor
         </tbody>
+        <tfoot class="hide">
+        <tr>
+            <td colspan="3">
+                <div class="alert">
+                    ${_('There are no permissions matching the search criteria.')}
+                </div>
+            </td>
+        </tr>
+        </tfoot>
     </table>
 </div>
 
@@ -70,29 +80,16 @@ ${layout.menubar(section='permissions', _=_)}
             "aoColumns": [
                 null,
                 null,
-                %if user.is_superuser:
-                    null,
-                    { "bSortable": false }
-                %else:
-                    null
-                %endif
+                null
             ]
         });
         $(".dataTables_wrapper").css("min-height","0");
         $(".dataTables_filter").hide();
 
-        $("#filterInput").keyup(function(){
-            $.each($(".permissionRow"), function(index, value) {
-                if($(value).data("search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
-                    $(value).hide(250);
-                }else{
-                    $(value).show(250);
-                }
-            });
-        });
-
         $("a[data-row-selector='true']").jHueRowSelector();
     });
 </script>
 
+${layout.commons()}
+
 ${commonfooter(messages)}

+ 72 - 46
apps/useradmin/src/useradmin/templates/list_users.mako

@@ -26,58 +26,71 @@ ${layout.menubar(section='users', _=_)}
 
 <div class="container-fluid">
     <h1>${_('Hue Users')}</h1>
-    <div class="well hueWell">
-        <div class="btn-group pull-right">
-            %if user.is_superuser == True:
-            <a href="${ url('useradmin.views.edit_user') }" class="btn">${_('Add user')}</a>
-            <a href="${ url('useradmin.views.add_ldap_user') }" class="btn">${_('Add/Sync LDAP user')}</a>
-            <a href="javascript:void(0)" class="btn confirmationModal" data-confirmation-url="${ url('useradmin.views.sync_ldap_users_groups') }">${_('Sync LDAP users/groups')}</a>
-            %endif
+    <div class="subnavContainer">
+        <div class="subnav sticky">
+            <p class="pull-right">
+                <input id="filterInput" type="text" class="input-xlarge search-query" placeholder="${_('Search for username, name, e-mail, etc...')}">
+            </p>
+            <p style="padding: 4px">
+                %if user.is_superuser:
+                    <button id="deleteUserBtn" class="btn confirmationModal" title="${_('Delete')}" disabled="disabled"><i class="icon-trash"></i> ${_('Delete')}</button>
+                    &nbsp;&nbsp;&nbsp;&nbsp;
+                    <a href="${ url('useradmin.views.edit_user') }" class="btn"><i class="icon-user"></i> ${_('Add user')}</a>
+                    <a href="${ url('useradmin.views.add_ldap_user') }" class="btn"><i class="icon-briefcase"></i> ${_('Add/Sync LDAP user')}</a>
+                    <a href="javascript:void(0)" class="btn confirmationModal" data-confirmation-url="${ url('useradmin.views.sync_ldap_users_groups') }"><i class="icon-refresh"></i> ${_('Sync LDAP users/groups')}</a>
+                    &nbsp;&nbsp;&nbsp;&nbsp;
+                %endif
+            </p>
         </div>
-        <form class="form-search">
-            ${_('Filter: ')}<input type="text" id="filterInput" class="input-xxlarge search-query" placeholder="${_('Search for username, name, e-mail, etc...')}">
-        </form>
     </div>
+    <br/>
+
     <table class="table table-striped table-condensed datatables">
         <thead>
             <tr>
+                %if user.is_superuser:
+                    <th width="1%"><input id="selectAll" type="checkbox"/></th>
+                %endif
                 <th>${_('Username')}</th>
                 <th>${_('First Name')}</th>
                 <th>${_('Last Name')}</th>
                 <th>${_('E-mail')}</th>
                 <th>${_('Groups')}</th>
                 <th>${_('Last Login')}</th>
-                <th>&nbsp;</th>
             </tr>
         </thead>
         <tbody>
         % for listed_user in users:
-            <tr class="userRow" data-search="${listed_user.username}${listed_user.first_name}${listed_user.last_name}${listed_user.email}${', '.join([group.name for group in listed_user.groups.all()])}">
-                <td>${listed_user.username}</td>
+            <tr class="tableRow" data-search="${listed_user.username}${listed_user.first_name}${listed_user.last_name}${listed_user.email}${', '.join([group.name for group in listed_user.groups.all()])}">
+                %if user.is_superuser:
+                    <td class="center" data-row-selector-exclude="true">
+                        <input type="checkbox" class="userCheck" data-username="${listed_user.username}" data-confirmation-url="${ url('useradmin.views.delete_user', username=urllib.quote(listed_user.username))}" data-row-selector-exclude="true"/>
+                    </td>
+                %endif
+                <td>
+                    %if user.is_superuser or user.username == listed_user.username:
+                        <h5><a title="${_('Edit %(username)s') % dict(username=listed_user.username)}" href="${ url('useradmin.views.edit_user', username=urllib.quote(listed_user.username)) }" data-row-selector="true">${listed_user.username}</a></h5>
+                    %else:
+                        <h5>${listed_user.username}</h5>
+                    %endif
+                </td>
                 <td>${listed_user.first_name}</td>
                 <td>${listed_user.last_name}</td>
                 <td>${listed_user.email}</td>
                 <td>${', '.join([group.name for group in listed_user.groups.all()])}</td>
                 <td>${date(listed_user.last_login)} ${time(listed_user.last_login).replace("p.m.","PM").replace("a.m.","AM")}</td>
-                <td class="right">
-                <%
-                    i18n_editUsername = _('Edit %(username)s') % {'username': listed_user.username}
-                    i18n_deleteUsername = _('Delete %(username)s') % {'username': listed_user.username}
-                %>
-                %if user.is_superuser == True:
-                    <a title="${_('Edit %(username)s') % dict(username=listed_user.username)}" class="btn small" href="${ url('useradmin.views.edit_user', username=urllib.quote(listed_user.username)) }" data-row-selector="true">${_('Edit')}</a>
-                    <a title="${_('Delete %(username)s') % dict(username=listed_user.username)}" class="btn small confirmationModal" alt="Are you sure you want to delete ${listed_user.username}?" href="javascript:void(0)" data-confirmation-url="${ url('useradmin.views.delete_user', username=urllib.quote_plus(listed_user.username)) }">${_('Delete')}</a>
-                %else:
-                    %if user.username == listed_user.username:
-                        <a title="${_('Edit %(username)s') % dict(username=listed_user.username)}" class="btn small" href="${ url('useradmin.views.edit_user', username=urllib.quote(listed_user.username)) }">Edit</a>
-                    %else:
-                        &nbsp;
-                    %endif
-                %endif
-                </td>
             </tr>
         % endfor
         </tbody>
+        <tfoot class="hide">
+            <tr>
+                <td colspan="8">
+                    <div class="alert">
+                        ${_('There are no users matching the search criteria.')}
+                    </div>
+                </td>
+            </tr>
+        </tfoot>
     </table>
 
     <div id="syncLdap" class="modal hide fade"></div>
@@ -94,14 +107,16 @@ ${layout.menubar(section='users', _=_)}
                 "bInfo": false,
                 "bFilter": false,
                 "aoColumns": [
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        { "sType": "date" },
-                        { "bSortable": false }
-                    ]
+                    %if user.is_superuser:
+                    { "bSortable": false },
+                    %endif
+                    null,
+                    null,
+                    null,
+                    null,
+                    null,
+                    { "sType": "date" }
+                ]
             });
             $(".dataTables_wrapper").css("min-height","0");
             $(".dataTables_filter").hide();
@@ -109,7 +124,7 @@ ${layout.menubar(section='users', _=_)}
             $(".confirmationModal").click(function(){
                 var _this = $(this);
                 $.ajax({
-                    url: _this.attr("data-confirmation-url"),
+                    url: _this.data("confirmation-url"),
                     beforeSend: function(xhr){
                         xhr.setRequestHeader("X-Requested-With", "Hue");
                     },
@@ -121,18 +136,29 @@ ${layout.menubar(section='users', _=_)}
                 });
             });
 
-            $("#filterInput").keyup(function(){
-                $.each($(".userRow"), function(index, value) {
-                  if($(value).data("search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
-                    $(value).hide(250);
-                  }else{
-                    $(value).show(250);
-                  }
-                });
+            $("#selectAll").change(function(){
+                if ($(this).is(":checked")){
+                    $(".userCheck").attr("checked", "checked");
+                }
+                else {
+                    $(".userCheck").removeAttr("checked");
+                }
+                $(".userCheck").change();
+            });
+
+            $(".userCheck").change(function(){
+                if ($(".userCheck:checked").length == 1){
+                    $("#deleteUserBtn").removeAttr("disabled").data("confirmation-url", $(".userCheck:checked").data("confirmation-url"));
+                }
+                else {
+                    $("#deleteUserBtn").attr("disabled", "disabled");
+                }
             });
 
             $("a[data-row-selector='true']").jHueRowSelector();
         });
     </script>
 
+${layout.commons()}
+
 ${commonfooter(messages)}

+ 4 - 3
desktop/core/static/css/hue2.css

@@ -884,11 +884,12 @@ div.box {
     width:100px;
     height:11px;
     border: 1px solid #CCCCCC;
-    border-radius: 5.5px;
-    -webkit-border-radius: 5.5px;
-    -moz-border-radius: 5.5px;
+    border-radius: 10px;
+    -webkit-border-radius: 10px;
+    -moz-border-radius: 10px;
     color: #555555;
     font-size: 10px;
+    padding-left: 4px;
 }
 
 .jHueSelectorBody {

+ 132 - 117
desktop/core/static/js/Source/jHue/jquery.selector.js

@@ -1,135 +1,150 @@
 /*
-* jHue selector plugin
-* it tranforms a select multiple into a searchable/selectable alphabetical list
-*/
+ * jHue selector plugin
+ * it tranforms a select multiple into a searchable/selectable alphabetical list
+ */
 ;(function ($, window, document, undefined) {
 
-	var pluginName = "jHueSelector",
-	defaults = {
-		selectAllLabel: "Select all",
-		searchPlaceholder: "Search",
-        width: 300
-	};
+    var pluginName = "jHueSelector",
+        defaults = {
+            selectAllLabel: "Select all",
+            searchPlaceholder: "Search",
+            noChoicesFound: "No choices found for this element",
+            width: 300,
+            height: 200
+        };
 
-	function Plugin(element, options) {
-		this.element = element;
-		this.options = $.extend({}, defaults, options) ;
-		this._defaults = defaults;
-		this._name = pluginName;
-		this.init();
-	}
+    function Plugin(element, options) {
+        this.element = element;
+        this.options = $.extend({}, defaults, options) ;
+        this._defaults = defaults;
+        this._name = pluginName;
+        this.init();
+    }
 
-	Plugin.prototype.setOptions = function(options) {
-		this.options = $.extend({}, defaults, options) ;
-	};
+    Plugin.prototype.setOptions = function(options) {
+        this.options = $.extend({}, defaults, options) ;
+    };
 
-	Plugin.prototype.init = function () {
-		var _this = this;
-		var addressBook = [];
-		var selectorContainer = $("<div>").addClass("jHueSelector");
-        if (this.options.width != 300){
+    Plugin.prototype.init = function () {
+        var _this = this;
+        var addressBook = [];
+        var selectorContainer = $("<div>");
+        if (this.options.width != 300) {
             selectorContainer.width(this.options.width);
         }
-		$(_this.element).hide();
-		$(_this.element).find("option").each(function(cnt, opt){
-			var initial = $(opt).text().substr(0,1).toLowerCase();
-			if (addressBook[initial] == null){
-				addressBook[initial] = [];
-			}
-			addressBook[initial].push($(opt));
-		});
-		var sortedKeys = [];
-		for (var key in addressBook){
-			sortedKeys.push(key);
-		}
-		sortedKeys.sort();
+        $(_this.element).hide();
+        $(_this.element).find("option").each(function (cnt, opt) {
+            var initial = $(opt).text().substr(0, 1).toLowerCase();
+            if (addressBook[initial] == null) {
+                addressBook[initial] = [];
+            }
+            addressBook[initial].push($(opt));
+        });
+        var sortedKeys = [];
+        for (var key in addressBook) {
+            sortedKeys.push(key);
+        }
+        sortedKeys.sort();
+
+        if (sortedKeys.length == 0){
+            $(_this.element).after($("<input>").attr("type", "text").attr("disabled", "disabled").val(this.options.noChoicesFound));
+        }
+        else {
+            selectorContainer.addClass("jHueSelector");
+            var body = $("<div>").addClass("jHueSelectorBody");
+            body.appendTo(selectorContainer);
+
+            for (var i = 0; i < sortedKeys.length; i++) {
+                var key = sortedKeys[i];
+                var ul = $("<ul>");
+                var dividerLi = $("<li>").addClass("selectorDivider");
+                dividerLi.html("<strong>" + key.toUpperCase() + "</strong>");
+                dividerLi.appendTo(ul);
+                $.each(addressBook[key], function (cnt, opt) {
+                    var li = $("<li>");
+                    var lbl = $("<label>").text(opt.text());
+                    var chk = $("<input>").attr("type", "checkbox").addClass("selector").change(function () {
+                        if ($(this).is(":checked")) {
+                            $(this).data("opt").attr("selected", "selected");
+                        }
+                        else {
+                            $(this).data("opt").removeAttr("selected");
+                        }
+                    }).data("opt", opt).prependTo(lbl);
+                    if (opt.is(":selected")) {
+                        chk.attr("checked", "checked");
+                    }
+                    lbl.appendTo(li);
+                    li.appendTo(ul);
+                });
+                ul.appendTo(body);
+            }
 
-		var body = $("<div>").addClass("jHueSelectorBody");
-		body.appendTo(selectorContainer);
+            var header = $("<div>").addClass("jHueSelectorHeader");
+            header.prependTo(selectorContainer);
 
-		for (var i=0; i<sortedKeys.length; i++){
-			var key = sortedKeys[i];
-			var ul = $("<ul>");
-			var dividerLi = $("<li>").addClass("selectorDivider");
-			dividerLi.html("<strong>"+key.toUpperCase()+"</strong>");
-			dividerLi.appendTo(ul);
-			$.each(addressBook[key], function(cnt, opt){
-				var li = $("<li>");
-				var lbl = $("<label>").text(opt.text());
-				var chk = $("<input>").attr("type","checkbox").addClass("selector").change(function(){
-					if ($(this).is(":checked")){
-						$(this).data("opt").attr("selected", "selected");
-					}
-					else {
-						$(this).data("opt").removeAttr("selected");
-					}
-				}).data("opt",opt).prependTo(lbl);
-                if (opt.is(":selected")){
-                    chk.attr("checked","checked");
+            var selectAll = $("<label>").text(this.options.selectAllLabel);
+            $("<input>").attr("type", "checkbox").change(function () {
+                var isChecked = $(this).is(":checked");
+                selectorContainer.find("input.selector:visible").each(function () {
+                    if (isChecked) {
+                        $(this).attr("checked", "checked");
+                        $(this).data("opt").attr("selected", "selected");
+                    }
+                    else {
+                        $(this).removeAttr("checked");
+                        $(this).data("opt").removeAttr("selected");
+                    }
+                });
+                if (searchBox.val() != "") {
+                    $(this).removeAttr("checked");
                 }
-				lbl.appendTo(li);
-				li.appendTo(ul);
-			});
-			ul.appendTo(body);
-		}
+            }).prependTo(selectAll);
+            selectAll.appendTo(header);
 
-		var header = $("<div>").addClass("jHueSelectorHeader");
-		header.prependTo(selectorContainer);
+            var searchBox = $("<input>").attr("type", "text").attr("placeholder", this.options.searchPlaceholder).keyup(function () {
+                body.find("ul").attr("show", true).show();
+                var q = $.trim($(this).val());
+                if (q != "") {
+                    body.find("li.selectorDivider").hide();
+                    body.find("label").each(function () {
+                        if ($(this).text().toLowerCase().indexOf(q) > -1) {
+                            $(this).parent().show();
+                        }
+                        else {
+                            $(this).parent().hide();
+                        }
+                    });
+                    body.find("ul").attr("show", false);
+                    body.find("ul > *:visible").parent().attr("show", true).find("li.selectorDivider").show();
+                }
+                else {
+                    body.find("li.selectorDivider").show();
+                    body.find("label").parent().show();
+                }
+                body.find("ul[show=false]").hide();
+                body.find("ul[show=true]").show();
+            });
+            if (this.options.width != 300) {
+                searchBox.css("margin-left", this.options.width - 120 + "px");
+            }
+            searchBox.prependTo(header);
 
-		var selectAll = $("<label>").text(this.options.selectAllLabel);
-		$("<input>").attr("type","checkbox").change(function(){
-			var isChecked = $(this).is(":checked");
-			selectorContainer.find("input.selector:visible").each(function(){
-				if (isChecked){
-					$(this).attr("checked", "checked");
-					$(this).data("opt").attr("selected", "selected");
-				}
-				else {
-					$(this).removeAttr("checked");
-					$(this).data("opt").removeAttr("selected");
-				}
-			});
-			if (searchBox.val() != ""){
-				$(this).removeAttr("checked");
-			}
-		}).prependTo(selectAll);
-		selectAll.appendTo(header);
+            body.height(this.options.height - header.outerHeight());
 
-		var searchBox = $("<input>").attr("type","text").attr("placeholder", this.options.searchPlaceholder).keyup(function(){
-			var q = $.trim($(this).val());
-			if (q != ""){
-				body.find("li.selectorDivider").hide();
-				body.find("label").each(function(){
-					if ($(this).text().toLowerCase().indexOf(q) > -1){
-						$(this).show();
-					}
-					else {
-						$(this).hide();
-					}
-				});
-			}
-			else {
-				body.find("li.selectorDivider").show();
-				body.find("label").show();
-			}
-		});
-        if (this.options.width != 300){
-            searchBox.css("margin-left", this.options.width-120+"px");
+            $(_this.element).after(selectorContainer);
         }
-		searchBox.prependTo(header);
-
-		$(_this.element).after(selectorContainer);
-	};
+    };
 
-	$.fn[pluginName] = function (options) {
-		return this.each(function () {
-			if (!$.data(this, 'plugin_' + pluginName)) {
-				$.data(this, 'plugin_' + pluginName, new Plugin( this, options));
-			}
-			else {
-				$.data(this, 'plugin_' + pluginName).setOptions(options);
-			}
-		});
-	}
+    $.fn[pluginName] = function (options) {
+        return this.each(function () {
+            if (!$.data(this, 'plugin_' + pluginName)) {
+                $.data(this, 'plugin_' + pluginName, new Plugin( this, options));
+            }
+            else {
+                $.data(this, 'plugin_' + pluginName).setOptions(options);
+            }
+        });
+    }
 
 })(jQuery, window, document);