Browse Source

HUE-744 [jb] Numeric/time columns are not sorted properly

Added new datatables sorting plugin
Fixed jb, beeswax and jobsub
Romain resolved conflicts:
	apps/jobbrowser/src/jobbrowser/templates/jobs_dock_info.mako
	apps/jobsub/src/jobsub/templates/status_bar.mako
Romain progress bars moved to HUE-734
Enrico Berti 13 years ago
parent
commit
f6a36f8bf9

+ 110 - 109
apps/beeswax/src/beeswax/templates/list_designs.mako

@@ -14,134 +14,135 @@
 ## See the License for the specific language governing permissions and
 ## limitations under the License.
 <%!
-from django.template.defaultfilters import timesince
-from desktop.views import commonheader, commonfooter
+    import time
+    from django.template.defaultfilters import timesince
+    from desktop.views import commonheader, commonfooter
 %>
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="layout" file="layout.mako" />
 ${commonheader("Beeswax: Queries", "beeswax", "100px")}
 ${layout.menubar(section='saved queries')}
 <div class="container-fluid">
-<h1>Beeswax: Queries</h1>
-  <table class="table table-striped table-condensed datatables">
-    <thead>
-      <tr>
-        <th>Name</th>
-		<th>Description</th>
-        <th>Owner</th>
-        <th>Type</th>
-        <th>Last Modified</th>
-		<th></th>
-      </tr>
-    </thead>
-    <tbody>
-    <%!
-      from beeswax import models
-    %>
-    % for design in page.object_list:
-      <%
-        may_edit = user == design.owner
-      %>
-      <tr>
-        <td>
-          % if may_edit:
-            % if design.type == models.SavedQuery.REPORT:
-              <a href="${ url('beeswax.views.edit_report', design_id=design.id) }" data-row-selector="true">${design.name}</a>
-            % else:
-              <a href="${ url('beeswax.views.execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a>
-            % endif
-          % else:
-            ${design.name}
-          % endif
-        </td>
-        <td>
-          % if design.desc:
-           <p>${design.desc}</p>
-          % endif
-        </td>
-        <td>${design.owner.username}</td>
-        <td>
-          % if design.type == models.SavedQuery.REPORT:
-            Report
-          % else:
-            Query
-          % endif
-        </td>
-        <td>
-          ${ timesince(design.mtime) } ago
-        </td>
-        <td>
-			<div class="btn-group">
-				<a href="#" data-toggle="dropdown" class="btn dropdown-toggle">
-					Options
-		    		<span class="caret"></span>
-		    	</a>
-				<ul class="dropdown-menu">
+    <h1>Beeswax: Queries</h1>
+    <table class="table table-striped table-condensed datatables">
+        <thead>
+        <tr>
+            <th>Name</th>
+            <th>Description</th>
+            <th>Owner</th>
+            <th>Type</th>
+            <th>Last Modified</th>
+            <th></th>
+        </tr>
+        </thead>
+        <tbody>
+            <%!
+                from beeswax import models
+            %>
+            % for design in page.object_list:
+                <%
+                    may_edit = user == design.owner
+                %>
+                <tr>
+                <td>
+                    % if may_edit:
+                    % if design.type == models.SavedQuery.REPORT:
+                            <a href="${ url('beeswax.views.edit_report', design_id=design.id) }" data-row-selector="true">${design.name}</a>
+                    % else:
+                            <a href="${ url('beeswax.views.execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a>
+                    % endif
+                    % else:
+                    ${design.name}
+                    % endif
+                </td>
+                <td>
+                    % if design.desc:
+                        <p>${design.desc}</p>
+                    % endif
+                </td>
+                    <td>${design.owner.username}</td>
+                <td>
+                    % if design.type == models.SavedQuery.REPORT:
+                        Report
+                    % else:
+                        Query
+                    % endif
+                </td>
+                    <td>
+                        <span alt="${time.mktime(design.mtime.timetuple())}">${ timesince(design.mtime) } ago</span>
+                    </td>
+                <td>
+                <div class="btn-group">
+                    <a href="#" data-toggle="dropdown" class="btn dropdown-toggle">
+                        Options
+                        <span class="caret"></span>
+                    </a>
+                <ul class="dropdown-menu">
 
-					% if may_edit:
-			             % if design.type == models.SavedQuery.REPORT:
-			               <li><a href="${ url('beeswax.views.edit_report', design_id=design.id) }" title="Edit this report." class="contextItem">Edit</a></li>
-			             % else:
-			               <li><a href="${ url('beeswax.views.execute_query', design_id=design.id) }" title="Edit this query." class="contextItem">Edit</a></li>
-			             % endif
-			             <li><a href="javascript:void(0)" data-confirmation-url="${ url('beeswax.views.delete_design', design_id=design.id) }" title="Delete this query." class="contextItem confirmationModal">Delete</a></li>
-			             <li><a href="${ url('beeswax.views.list_query_history') }?design_id=${design.id}" title="View the usage history of this query." class="contextItem">Usage History</a></li>
+                % if may_edit:
+                    % if design.type == models.SavedQuery.REPORT:
+                            <li><a href="${ url('beeswax.views.edit_report', design_id=design.id) }" title="Edit this report." class="contextItem">Edit</a></li>
+                    % else:
+                            <li><a href="${ url('beeswax.views.execute_query', design_id=design.id) }" title="Edit this query." class="contextItem">Edit</a></li>
+                    % endif
+                        <li><a href="javascript:void(0)" data-confirmation-url="${ url('beeswax.views.delete_design', design_id=design.id) }" title="Delete this query." class="contextItem confirmationModal">Delete</a></li>
+                        <li><a href="${ url('beeswax.views.list_query_history') }?design_id=${design.id}" title="View the usage history of this query." class="contextItem">Usage History</a></li>
 
-			        % endif
-						<li><a href="${ url('beeswax.views.clone_design', design_id=design.id) }" title="Copy this query." class="contextItem">Clone</a></li>
-		    	</ul>
-		    </div>
+                % endif
+                    <li><a href="${ url('beeswax.views.clone_design', design_id=design.id) }" title="Copy this query." class="contextItem">Clone</a></li>
+                </ul>
+                </div>
 
 
-        </td>
-      </tr>
-    % endfor
-    </tbody>
-  </table>
-${comps.pagination(page)}
+                </td>
+                </tr>
+            % endfor
+        </tbody>
+    </table>
+    ${comps.pagination(page)}
 </div>
 
 <div id="deleteQuery" class="modal hide fade">
-	<form id="deleteQueryForm" action="" method="POST">
-	<div class="modal-header">
-		<a href="#" class="close" data-dismiss="modal">&times;</a>
-		<h3 id="deleteQueryMessage">Confirm action</h3>
-	</div>
-	<div class="modal-footer">
-		<input type="submit" class="btn primary" value="Yes"/>
-		<a href="#" class="btn secondary" data-dismiss="modal">No</a>
-	</div>
-	</form>
+    <form id="deleteQueryForm" action="" method="POST">
+        <div class="modal-header">
+            <a href="#" class="close" data-dismiss="modal">&times;</a>
+            <h3 id="deleteQueryMessage">Confirm action</h3>
+        </div>
+        <div class="modal-footer">
+            <input type="submit" class="btn primary" value="Yes"/>
+            <a href="#" class="btn secondary" data-dismiss="modal">No</a>
+        </div>
+    </form>
 </div>
 
 <script type="text/javascript" charset="utf-8">
-	$(document).ready(function(){
+    $(document).ready(function(){
 
-		$(".datatables").dataTable({
-			"bPaginate": false,
-		    "bLengthChange": false,
-			"bInfo": false,
-			"bFilter": false,
-			"aoColumns": [
-				null,
-				null,
-				null,
-				null,
-				null,
-				{ "bSortable": false }
-			]
-		});
+        $(".datatables").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bFilter": false,
+            "aoColumns": [
+                null,
+                null,
+                null,
+                null,
+                { "sType": "alt-numeric" },
+                { "bSortable": false }
+            ]
+        });
 
-		$(".confirmationModal").live("click", function(){
-			$.getJSON($(this).attr("data-confirmation-url"), function(data){
-				$("#deleteQueryForm").attr("action", data.url);
-				$("#deleteQueryMessage").text(data.title);
-			});
-			$("#deleteQuery").modal("show");
-		});
+        $(".confirmationModal").live("click", function(){
+            $.getJSON($(this).attr("data-confirmation-url"), function(data){
+                $("#deleteQueryForm").attr("action", data.url);
+                $("#deleteQueryMessage").text(data.title);
+            });
+            $("#deleteQuery").modal("show");
+        });
 
-		$("a[data-row-selector='true']").jHueRowSelector();
-	});
+        $("a[data-row-selector='true']").jHueRowSelector();
+    });
 </script>
 
 ${commonfooter()}

+ 19 - 13
apps/beeswax/src/beeswax/templates/list_history.mako

@@ -14,6 +14,7 @@
 ## See the License for the specific language governing permissions and
 ## limitations under the License.
 <%!
+import time
 from desktop.views import commonheader, commonfooter
 %>
 <%namespace name="layout" file="layout.mako" />
@@ -112,7 +113,7 @@ ${layout.menubar(section='history')}
 			  pass
 		      %>
 		      <tr class="histRow">
-		        <td>${query.submission_date.strftime("%x %X")}</td>
+                <td><span alt="${time.mktime(query.submission_date.timetuple())}">${query.submission_date.strftime("%x %X")}</span></td>
 		        <td>${show_saved_query(design, query)}</td>
 		        <td>
 		          <p>
@@ -141,19 +142,24 @@ ${layout.menubar(section='history')}
 	</div>
 </div>
 
-
 <script type="text/javascript" charset="utf-8">
-	$(document).ready(function(){
-
-		$(".datatables").dataTable({
-			"bPaginate": false,
-		    "bLengthChange": false,
-			"bInfo": false,
-			"bFilter": false
-		});
-
-		$("a[data-row-selector='true']").jHueRowSelector();
+    $(document).ready(function(){
+        $(".datatables").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bFilter": false,
+            "aoColumns": [
+                { "sType": "alt-numeric" },
+                null,
+                null,
+                null,
+                null,
+                { "bSortable": false }
+            ]
+        });
 
-	});
+        $("a[data-row-selector='true']").jHueRowSelector();
+    });
 </script>
 ${commonfooter()}

+ 212 - 162
apps/beeswax/src/beeswax/templates/my_queries.mako

@@ -14,6 +14,7 @@
 ## See the License for the specific language governing permissions and
 ## limitations under the License.
 <%!
+import time
 from django.template.defaultfilters import timesince
 from desktop.views import commonheader, commonfooter
 %>
@@ -23,185 +24,234 @@ from desktop.views import commonheader, commonfooter
 ${commonheader("Beeswax: My Queries", "beeswax", "100px")}
 ${layout.menubar(section='my queries')}
 <style>
-	.tab-content {
-		overflow:visible!important;
-	}
+    .tab-content {
+        overflow:visible!important;
+    }
 </style>
 <div class="container-fluid">
-	<h1>Beeswax: My Queries</h1>
+    <h1>Beeswax: My Queries</h1>
 
-	<ul class="nav nav-tabs">
-		<li class="active"><a href="#recentSavedQueries" data-toggle="tab">Recent Saved Queries</a></li>
-		<li><a href="#recentRunQueries" data-toggle="tab">Recent Run Queries</a></li>
-	</ul>
+    <div class="well hueWell">
+        <div class="btn-group pull-right">
+            <a class="btn" href="/beeswax/">Create New Query</a>
+        </div>
 
+        <form class="form-search">
+            Filter: <input placeholder="Search for name, description, etc..." class="input-xlarge search-query" id="filterInput">
+        </form>
+    </div>
 
-		<div class="tab-content">
-			<div class="active tab-pane" id="recentSavedQueries">
+    <ul class="nav nav-tabs">
+        <li class="active"><a href="#recentSavedQueries" data-toggle="tab">Recent Saved Queries &nbsp;<span id="recentSavedQueriesFilterCnt" class="badge badge-info hide"></span></a></li>
+        <li><a href="#recentRunQueries" data-toggle="tab">Recent Run Queries  &nbsp;<span id="recentRunQueriesFilterCnt" class="badge badge-info hide"></span></a></li>
+    </ul>
 
-				 <table class="table table-striped table-condensed datatables">
-			          <thead>
-			            <tr>
-			              <th>Name</th>
-						  <th>Desc</th>
-			              <th>Type</th>
-			              <th>Last Modified</th>
-						  <th></th>
-			            </tr>
-			          </thead>
-			          <tbody>
-			          <%!
-			            from beeswax import models
-			          %>
-			          % for design in q_page.object_list:
-			            <tr>
-			              <td>
-			                % if design.type == models.SavedQuery.REPORT:
-			                  <a href="${ url('beeswax.views.edit_report', design_id=design.id) }" data-row-selector="true">${design.name}</a>
-			                % else:
-			                  <a href="${ url('beeswax.views.execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a>
-			                % endif
-			              </td>
-			              <td>
-			                % if design.desc:
-			                 <p>${design.desc}</p>
-			                % endif
-			              </td>
-			              <td>
-			                % if design.type == models.SavedQuery.REPORT:
-			                  Report
-			                % else:
-			                  Query
-			                % endif
-			              </td>
-			              <td>
-			                ${ timesince(design.mtime) } ago
-			              </td>
-			              <td>
-							<div class="btn-group">
-								<a href="#" data-toggle="dropdown" class="btn dropdown-toggle">
-									Options
-						    		<span class="caret"></span>
-						    	</a>
-								<ul class="dropdown-menu">
-									% if design.type == models.SavedQuery.REPORT:
-					                    <li><a href="${ url('beeswax.views.edit_report', design_id=design.id) }" title="Edit this report." class="contextItem">Edit</a></li>
-					                  % else:
-					                    <li><a href="${ url('beeswax.views.execute_query', design_id=design.id) }" title="Edit this query." class="contextItem">Edit</a></li>
-					                  % endif
-					                  <li><a href="javascript:void(0)" data-confirmation-url="${ url('beeswax.views.delete_design', design_id=design.id) }" title="Delete this query." class="contextItem confirmationModal">Delete</a></li>
-					                  <li><a href="${ url('beeswax.views.list_query_history') }?design_id=${design.id}" title="View the usage history of this query." class="contextItem">Usage History</a></li>
-					                  <li><a href="${ url('beeswax.views.clone_design', design_id=design.id) }" title="Copy this query." class="contextItem">Clone</a></li>
-						    	</ul>
-						    </div>
-			              </td>
-			            </tr>
-			          % endfor
-			          </tbody>
-			        </table>
-			        % if q_page.number != q_page.num_pages():
-			          <a href="${ url('beeswax.views.list_designs') }?user=${request.user.username|u}" >View all my queries &raquo;</a>
-			        % endif
-			</div>
+    <div class="tab-content">
+            <div class="active tab-pane" id="recentSavedQueries">
 
-			<div class="tab-pane" id="recentRunQueries">
-				<table class="table table-striped table-condensed datatables">
-		          <thead>
-		            <tr>
-		              <th>Time</th>
-		              <th>Name</th>
-		              <th>Query</th>
-		              <th>State</th>
-		              <th>Result</th>
-		            </tr>
-		          </thead>
-		          <tbody>
-		          <%!
-		            from beeswax import models, views
-		          %>
-		          % for query in h_page.object_list:
-		            <%
-			      qcontext = ""
-			      try:
-				design = query.design
-				qcontext = views.make_query_context('design', design.id)
-			      except:
-				pass
-		            %>
-		            <tr >
-		              <td>${query.submission_date.strftime("%x %X")}</td>
-		              ## TODO (bc): Only showing HQL (not REPORT)
-		              <td><a href="${ url('beeswax.views.execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a></td>
-		              <td>
-		                <p>
-		                  % if len(query.query) > 100:
-		                    <code>${collapse_whitespace(query.query[:100])}...</code>
-		                  % else:
-		                    <code>${collapse_whitespace(query.query)}</code>
-		                  % endif
-		                </p>
-		              </td>
-		              <td>${models.QueryHistory.STATE[query.last_state]}</td>
-		              <td>
-		                % if qcontext and query.last_state != models.QueryHistory.STATE.expired.index:
-		                  <a href="${ url('beeswax.views.watch_query', id=query.id) }?context=${qcontext|u}">View</a>
-		                % else:
-		                  ~
-		                % endif
-		              </td>
-		            </tr>
-		          % endfor
-		          </tbody>
-		        </table>
-		        % if h_page.number != h_page.num_pages():
-		          <a href="${ url('beeswax.views.list_query_history') }">View my entire query history &raquo;</a>
-		        % endif
-			</div>
+                 <table id="recentSavedQueriesTable" class="table table-striped table-condensed datatables">
+                      <thead>
+                        <tr>
+                          <th>Name</th>
+                          <th>Desc</th>
+                          <th>Type</th>
+                          <th>Last Modified</th>
+                          <th></th>
+                        </tr>
+                      </thead>
+                      <tbody>
+                      <%!
+                        from beeswax import models
+                      %>
+                      % for design in q_page.object_list:
+                        <tr>
+                          <td>
+                            % if design.type == models.SavedQuery.REPORT:
+                              <a href="${ url('beeswax.views.edit_report', design_id=design.id) }" data-row-selector="true">${design.name}</a>
+                            % else:
+                              <a href="${ url('beeswax.views.execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a>
+                            % endif
+                          </td>
+                          <td>
+                            % if design.desc:
+                             <p>${design.desc}</p>
+                            % endif
+                          </td>
+                          <td>
+                            % if design.type == models.SavedQuery.REPORT:
+                              Report
+                            % else:
+                              Query
+                            % endif
+                          </td>
+                          <td>
+                              <span alt="${time.mktime(design.mtime.timetuple())}">${ timesince(design.mtime) } ago</span>
+                          </td>
+                          <td>
+                            <div class="btn-group">
+                                <a href="#" data-toggle="dropdown" class="btn dropdown-toggle">
+                                       Options
+                                    <span class="caret"></span>
+                                </a>
+                                <ul class="dropdown-menu">
+                                      % if design.type == models.SavedQuery.REPORT:
+                                        <li><a href="${ url('beeswax.views.edit_report', design_id=design.id) }" title="Edit this report." class="contextItem">Edit</a></li>
+                                      % else:
+                                        <li><a href="${ url('beeswax.views.execute_query', design_id=design.id) }" title="Edit this query." class="contextItem">Edit</a></li>
+                                      % endif
+                                         <li><a href="javascript:void(0)" data-confirmation-url="${ url('beeswax.views.delete_design', design_id=design.id) }" title="Delete this query." class="contextItem confirmationModal">Delete</a></li>
+                                      <li><a href="${ url('beeswax.views.list_query_history') }?design_id=${design.id}" title="View the usage history of this query." class="contextItem">Usage History</a></li>
+                                      <li><a href="${ url('beeswax.views.clone_design', design_id=design.id) }" title="Copy this query." class="contextItem">Clone</a></li>
+                                </ul>
+                            </div>
+                          </td>
+                        </tr>
+                      % endfor
+                      </tbody>
+                    </table>
+                    % if q_page.number != q_page.num_pages():
+                      <a href="${ url('beeswax.views.list_designs') }?user=${request.user.username|u}" >View all my queries &raquo;</a>
+                    % endif
+            </div>
 
+            <div class="tab-pane" id="recentRunQueries">
+                <table id="recentRunQueriesTable" class="table table-striped table-condensed datatables">
+                  <thead>
+                    <tr>
+                      <th>Time</th>
+                      <th>Name</th>
+                      <th>Query</th>
+                      <th>State</th>
+                      <th>Result</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                  <%!
+                    from beeswax import models, views
+                  %>
+                  % for query in h_page.object_list:
+                    <%
+                    qcontext = ""
+                    try:
+                      design = query.design
+                      qcontext = views.make_query_context('design', design.id)
+                    except:
+                      pass
+                    %>
+                    <tr>
+                      <td><span alt="${time.mktime(query.submission_date.timetuple())}">${query.submission_date.strftime("%x %X")}</span></td>
+                      ## TODO (bc): Only showing HQL (not REPORT)
+                      <td><a href="${ url('beeswax.views.execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a></td>
+                      <td>
+                        <p>
+                          % if len(query.query) > 100:
+                            <code>${collapse_whitespace(query.query[:100])}...</code>
+                          % else:
+                            <code>${collapse_whitespace(query.query)}</code>
+                          % endif
+                        </p>
+                      </td>
+                      <td>${models.QueryHistory.STATE[query.last_state]}</td>
+                      <td>
+                        % if qcontext and query.last_state != models.QueryHistory.STATE.expired.index:
+                          <a href="${ url('beeswax.views.watch_query', id=query.id) }?context=${qcontext|u}">View</a>
+                        % else:
+                          ~
+                        % endif
+                      </td>
+                    </tr>
+                  % endfor
+                  </tbody>
+                </table>
+                % if h_page.number != h_page.num_pages():
+                  <a href="${ url('beeswax.views.list_query_history') }">View my entire query history &raquo;</a>
+                % endif
+            </div>
+    </div>
 </div>
 
 <div id="deleteQuery" class="modal hide fade">
-	<form id="deleteQueryForm" action="" method="POST">
-	<div class="modal-header">
-		<a href="#" class="close" data-dismiss="modal">&times;</a>
-		<h3 id="deleteQueryMessage">Confirm action</h3>
-	</div>
-	<div class="modal-footer">
-		<input type="submit" class="btn primary" value="Yes"/>
-		<a href="#" class="btn secondary" data-dismiss="modal">No</a>
-	</div>
-	</form>
-</div>
+    <form id="deleteQueryForm" action="" method="POST">
+    <div class="modal-header">
+        <a href="#" class="close" data-dismiss="modal">&times;</a>
+        <h3 id="deleteQueryMessage">Confirm action</h3>
+    </div>
+    <div class="modal-footer">
+        <input type="submit" class="btn primary" value="Yes"/>
+        <a href="#" class="btn secondary" data-dismiss="modal">No</a>
+    </div>
+    </form>
 </div>
 
 <script type="text/javascript" charset="utf-8">
-	$(document).ready(function(){
-		$(".datatables").dataTable({
-			"bPaginate": false,
-		    "bLengthChange": false,
-			"bInfo": false,
-			"bFilter": false,
-			"aoColumns": [
-				null,
-				null,
-				null,
-				null,
-				{ "bSortable": false }
-			]
-		});
+    $(document).ready(function(){
+        var recentSavedQueries = $("#recentSavedQueriesTable").dataTable({
+            "sDom": "<'row'r>t<'row'<'span8'i><''p>>",
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "aoColumns": [
+                null,
+                null,
+                null,
+                { "sType": "alt-numeric"},
+                { "bSortable": false }
+            ]
+        });
 
-		$(".confirmationModal").live("click", function(){
-			$.getJSON($(this).attr("data-confirmation-url"), function(data){
-				$("#deleteQueryForm").attr("action", data.url);
-				$("#deleteQueryMessage").text(data.title);
-			});
-			$("#deleteQuery").modal("show");
-		});
+        var recentRunQueries = $("#recentRunQueriesTable").dataTable({
+            "sDom": "<'row'r>t<'row'<'span8'i><''p>>",
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "aoColumns": [
+                { "sType": "alt-numeric"},
+                null,
+                null,
+                null,
+                { "bSortable": false }
+            ]
+        });
 
-		$("a[data-row-selector='true']").jHueRowSelector();
 
-	});
-</script>
+        $("#filterInput").keyup(function() {
+            recentSavedQueries.fnFilter($(this).val());
+            recentRunQueries.fnFilter($(this).val());
+            if ($.trim($(this).val()) != ""){
+                var recentSavedQueriesCnt = $("#recentSavedQueriesTable tbody tr").length;
+                var isRecentSavedQueriesEmpty = ($("#recentSavedQueries tbody tr td.dataTables_empty").length == 1)
+                if (recentSavedQueriesCnt > 0 && !isRecentSavedQueriesEmpty){
+                    $("#recentSavedQueriesFilterCnt").text(recentSavedQueriesCnt).show();
+                }
+                else {
+                    $("#recentSavedQueriesFilterCnt").hide().text("");
+                }
+
+                var recentRunQueriesCnt = $("#recentRunQueriesTable tbody tr").length;
+                var isRecentRunQueriesEmpty = ($("#recentRunQueriesTable tbody tr td.dataTables_empty").length == 1)
+                if (recentRunQueriesCnt > 0 && !isRecentRunQueriesEmpty){
+                    $("#recentRunQueriesFilterCnt").text(recentRunQueriesCnt).show();
+                }
+                else {
+                    $("#recentRunQueriesFilterCnt").hide().text("");
+                }
+            }
+            else {
+                $("#recentSavedQueriesFilterCnt").hide().text("");
+                $("#recentRunQueriesFilterCnt").hide().text("");
+            }
+        });
 
+        $(".confirmationModal").live("click", function(){
+            $.getJSON($(this).attr("data-confirmation-url"), function(data){
+                $("#deleteQueryForm").attr("action", data.url);
+                $("#deleteQueryMessage").text(data.title);
+            });
+            $("#deleteQuery").modal("show");
+        });
+
+        $("a[data-row-selector='true']").jHueRowSelector();
+    });
+</script>
 
 ${commonfooter()}

+ 159 - 116
apps/jobbrowser/src/jobbrowser/templates/attempt.mako

@@ -22,127 +22,170 @@ ${commonheader("Task Attempt: " + attempt.attemptId + ":: Job Browser", "jobbrow
 <div class="container-fluid">
     <h1>Task Attempt: ${attempt.attemptId} :: Job Browser</h1>
     <div class="row-fluid">
-      <div class="span2">
-        <div class="well sidebar-nav">
-          <h6>Attempt ID</h6>
-          ${attempt.attemptId_short}
-
-          <h6>Task</h6>
-          <a href="${url('jobbrowser.views.single_task', jobid=joblnk.jobId, taskid=taskid)}"
-                class="frame_tip jt_view" title="View this task">${task.taskId_short}</a>
-
-          <h6>Job</h6>
-          <a href="${url('jobbrowser.views.single_job', jobid=joblnk.jobId)}"
-                class="frame_tip jt_view" title="View this job">${joblnk.jobId_short}</a>
-
-          <h6>Status</h6>
-          ${attempt.state.lower()}
+        <div class="span2">
+            <div class="well sidebar-nav">
+                <ul class="nav nav-list">
+                    <li class="nav-header">Attempt ID</li>
+                    <li>${attempt.attemptId_short}</li>
+                    <li class="nav-header">Task</li>
+                    <li><a href="${url('jobbrowser.views.single_task', jobid=joblnk.jobId, taskid=taskid)}" title="View this task">${task.taskId_short}</a>
+                    </li>
+                    <li class="nav-header">Job</li>
+                    <li><a href="${url('jobbrowser.views.single_job', jobid=joblnk.jobId)}" title="View this job">${joblnk.jobId_short}</a></li>
+                    <li class="nav-header">Status</li>
+                    <li>
+                        <%
+                            status = attempt.state.lower()
+                        %>
+                        % if status == 'running' or status == 'pending':
+                                <span class="label label-warning">${status}</span>
+                        % elif status == 'succeeded':
+                                <span class="label label-success">${status}</span>
+                        % else:
+                                <span class="label">${status}</span>
+                        % endif
+                    </li>
+                </ul>
+            </div>
         </div>
-      </div>
 
-      <div class="span10">
-        <ul class="nav nav-tabs">
-          <li class="active"><a href="#metadata" data-toggle="tab">Metadata</a></li>
-          <li><a href="#counters" data-toggle="tab">Counters</a></li>
-          <li><a href="#logs" data-toggle="tab">Logs</a></li>
-        </ul>
+        <div class="span10">
+            <ul class="nav nav-tabs">
+                <li class="active"><a href="#metadata" data-toggle="tab">Metadata</a></li>
+                <li><a href="#counters" data-toggle="tab">Counters</a></li>
+                <li><a href="#logs" data-toggle="tab">Logs</a></li>
+            </ul>
 
-        <div class="tab-content">
-          <div class="tab-pane active" id="metadata">
-            <table class="table">
-              <tbody>
-                <tr>
-                  <td>Attempt id</td>
-                  <td>${attempt.attemptId}</td>
-                </tr>
-                <tr>
-                  <td>Task id</td>
-                  <td><a href="${url('jobbrowser.views.single_task', jobid=joblnk.jobId, taskid=taskid)}" class="frame_tip jt_view" title="View this task">${task.taskId}</a></td>
-                </tr>
-                <tr>
-                  <td>Task Type</td>
-                  <td>${task.taskType}</td>
-                </tr>
-                <tr>
-                  <td>JobId</td>
-                  <td><a href="${url('jobbrowser.views.single_job', jobid=joblnk.jobId)}" class="frame_tip jt_view" title="View this job">${joblnk.jobId}</a></td>
-                </tr>
-                <tr>
-                  <td>State</td>
-                  <td>${attempt.state}</td>
-                </tr>
-                <tr>
-                  <td>Start Time</td>
-                  <td>${attempt.startTimeFormatted}</td>
-                </tr>
-                <tr>
-                  <td>Finish Time</td>
-                  <td>${attempt.finishTimeFormatted}</td>
-                </tr>
-                <tr>
-                  <td>Progress</td>
-                  <td>${"%d" % (attempt.progress * 100)}%</td>
-                </tr>
-                <tr>
-                  <td>Task Tracker</td>
-                  <td><a href="/jobbrowser/trackers/${attempt.taskTrackerId}">${attempt.taskTrackerId}</a></td>
-                </tr>
-                <tr>
-                  <td>Phase</td>
-                  <td>${attempt.phase}</td>
-                </tr>
-                <tr>
-                  <td>Output Size</td>
-                  <td>${attempt.outputSize}</td>
-                </tr>
-              </tbody>
-            </table>
-          </div>
+            <div class="tab-content">
+                <div class="tab-pane active" id="metadata">
+                    <table id="metadataTable" class="table table-striped table-condensed">
+                        <thead>
+                        <tr>
+                            <th>Name</th>
+                            <th>Value</th>
+                        </tr>
+                        </thead>
+                        <tbody>
+                        <tr>
+                            <td>Attempt id</td>
+                            <td>${attempt.attemptId}</td>
+                        </tr>
+                        <tr>
+                            <td>Task id</td>
+                            <td><a href="${url('jobbrowser.views.single_task', jobid=joblnk.jobId, taskid=taskid)}" title="View this task">${task.taskId}</a></td>
+                        </tr>
+                        <tr>
+                            <td>Task Type</td>
+                            <td>${task.taskType}</td>
+                        </tr>
+                        <tr>
+                            <td>JobId</td>
+                            <td><a href="${url('jobbrowser.views.single_job', jobid=joblnk.jobId)}" title="View this job">${joblnk.jobId}</a></td>
+                        </tr>
+                        <tr>
+                            <td>State</td>
+                            <td>${attempt.state}</td>
+                        </tr>
+                        <tr>
+                            <td>Start Time</td>
+                            <td>${attempt.startTimeFormatted}</td>
+                        </tr>
+                        <tr>
+                            <td>Finish Time</td>
+                            <td>${attempt.finishTimeFormatted}</td>
+                        </tr>
+                        <tr>
+                            <td>Progress</td>
+                            <td>${"%d" % (attempt.progress * 100)}%</td>
+                        </tr>
+                        <tr>
+                            <td>Task Tracker</td>
+                            <td><a href="/jobbrowser/trackers/${attempt.taskTrackerId}">${attempt.taskTrackerId}</a></td>
+                        </tr>
+                        <tr>
+                            <td>Phase</td>
+                            <td>${attempt.phase}</td>
+                        </tr>
+                        <tr>
+                            <td>Output Size</td>
+                            <td>${attempt.outputSize}</td>
+                        </tr>
+                        </tbody>
+                    </table>
+                </div>
 
-          <div class="tab-pane" id="counters">
-            ${comps.task_counters(task.counters)}
-          </div>
+                <div class="tab-pane" id="counters">
+                    ${comps.task_counters(task.counters)}
+                </div>
 
-          <div class="tab-pane jt-logs" id="logs">
-            <%
-              log_diagnostic = logs[0]
-              log_stdout = logs[1]
-              log_stderr = logs[2]
-              log_syslog = logs[3]
-            %>
-  <%def name="format_log(raw)">
-  ## have to remove any indentation here or it breaks inside the pre tags
-  % for line in raw.split('\n'):
-  ${ line | h,trim }
-  % endfor
-  </%def>
-            <h2>task diagnostic log</h2>
-            % if not log_diagnostic:
-  <pre>-- empty --</pre>
-            % else:
-  <pre>${format_log(log_diagnostic)}</pre>
-            % endif
-            <h2>stdout</h2>
-            % if not log_stdout:
-  <pre>-- empty --</pre>
-            % else:
-  <pre>${format_log(log_stdout)}</pre>
-            % endif
-            <h2>stderr</h2>
-            % if not log_stderr:
-  <pre>-- empty --</pre>
-            % else:
-  <pre>${format_log(log_stderr)}</pre>
-            % endif
-            <h2>syslog</h2>
-            % if not log_syslog:
-  <pre>-- empty --</pre>
-            % else:
-  <pre>${format_log(log_syslog)}</pre>
-            % endif
-          </div>
+                <div class="tab-pane jt-logs" id="logs">
+                    <%
+                        log_diagnostic = logs[0]
+                        log_stdout = logs[1]
+                        log_stderr = logs[2]
+                        log_syslog = logs[3]
+                    %>
+                    <%def name="format_log(raw)">
+                        ## have to remove any indentation here or it breaks inside the pre tags
+                          % for line in raw.split('\n'):
+                              ${ line | h,trim }
+                        % endfor
+                    </%def>
+                    <h2>task diagnostic log</h2>
+                    % if not log_diagnostic:
+                            <pre>-- empty --</pre>
+                    % else:
+                            <pre>${format_log(log_diagnostic)}</pre>
+                    % endif
+                    <h2>stdout</h2>
+                    % if not log_stdout:
+                            <pre>-- empty --</pre>
+                    % else:
+                            <pre>${format_log(log_stdout)}</pre>
+                    % endif
+                    <h2>stderr</h2>
+                    % if not log_stderr:
+                            <pre>-- empty --</pre>
+                    % else:
+                            <pre>${format_log(log_stderr)}</pre>
+                    % endif
+                    <h2>syslog</h2>
+                    % if not log_syslog:
+                            <pre>-- empty --</pre>
+                    % else:
+                            <pre>${format_log(log_syslog)}</pre>
+                    % endif
+                </div>
+            </div>
         </div>
-      </div>
     </div>
 </div>
-${commonfooter()}
+
+<script type="text/javascript" charset="utf-8">
+    $(document).ready(function(){
+        $("#metadataTable").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bAutoWidth": false,
+            "bFilter": false,
+            "aoColumns": [
+                { "sWidth": "30%" },
+                { "sWidth": "70%" }
+            ]
+        });
+
+        $(".taskCountersTable").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bFilter": false,
+            "bAutoWidth": false,
+            "aoColumns": [
+                { "sWidth": "30%" },
+                { "sWidth": "70%" }
+            ]
+        });
+    });
+</script>
+${commonfooter()}

+ 237 - 240
apps/jobbrowser/src/jobbrowser/templates/job.mako

@@ -24,276 +24,273 @@
 %>
 
 <%def name="task_table(tasks)">
-  <table class="taskTable table table-striped table-condensed">
-    <thead>
-      <tr>
-        <th>Tasks</th>
-        <th>Type</th>
-		<th>&nbsp;</th>
-      </tr>
-    </thead>
-    <tbody>
-      % for task in tasks:
+    <table class="taskTable table table-striped table-condensed">
+        <thead>
         <tr>
-			<td>${task.taskId_short}</td>
-			<td>${task.taskType}</td>
-          	<td>
-				<a title="View this task" href="${ url('jobbrowser.views.single_task', jobid=job.jobId, taskid=task.taskId) }">View</a>
-			</td>
+            <th>Tasks</th>
+            <th>Type</th>
+            <th>&nbsp;</th>
         </tr>
-      % endfor
-    </tbody>
-  </table>
+        </thead>
+        <tbody>
+                % for task in tasks:
+                <tr>
+                    <td>${task.taskId_short}</td>
+                    <td>${task.taskType}</td>
+                    <td>
+                        <a title="View this task" href="${ url('jobbrowser.views.single_task', jobid=job.jobId, taskid=task.taskId) }">View</a>
+                    </td>
+                </tr>
+                % endfor
+        </tbody>
+    </table>
 </%def>
 <%def name="rows_for_conf_vars(rows)">
     %  for k, v in rows.iteritems():
-      <tr>
-          <td>${format_counter_name(k)}</td>
-          <%
+        <tr>
+            <td>${format_counter_name(k)}</td>
+        <%
             splitArray = v.split(",")
-          %>
-            <td>
+        %>
+        <td>
             % for i, val in enumerate(splitArray):
-               <%
-                  url_splitted = request.fs.urlsplit(val)
-                  is_hdfs_uri = bool(url_splitted[1])
-               %>
-               % if is_hdfs_uri:
-                  <%
+            <%
+                url_splitted = request.fs.urlsplit(val)
+                is_hdfs_uri = bool(url_splitted[1])
+            %>
+            % if is_hdfs_uri:
+                <%
                     if request.fs.isfile(url_splitted[2]):
                       target = "FileViewer"
                     else:
                       target = "FileBrowser"
-                  %>
-                  <a href="${location_to_url(request, val)}" title="${val}" target="${target}">${val}</a>
-                  % if i != len(splitArray) - 1:
-                    <br>
-                  % endif
-               % else:
-                  ${val}
-               % endif
+                %>
+                    <a href="${location_to_url(request, val)}" title="${val}" target="${target}">${val}</a>
+                % if i != len(splitArray) - 1:
+                        <br>
+                % endif
+            % else:
+                ${val}
+            % endif
             % endfor
-            </td>
+        </td>
         </tr>
     % endfor
 </%def>
 ${commonheader("Job: " + job.jobId + " - Job Browser", "jobbrowser")}
 
 <div class="container-fluid">
-	<h1>Job: ${job.jobId} - Job Browser</h1>
-	<div class="row-fluid">
-		<div class="span2">
-			<div class="well sidebar-nav">
-				<h6>Job ID</h6>
-				${job.jobId}
-
-				<h6>User</h6>
-				${job.user}
-
-				<h6>Status</h6>
-				% if job.status.lower() == 'running' or job.status.lower() == 'pending':
-					<span class="label label-warning">${job.status.lower()}</span>
-				% elif job.status.lower() == 'succeeded':
-					<span class="label label-success">${job.status.lower()}</span>
-				% else:
-					<span class="label">${job.status.lower()}</span>
-				% endif
-
-				% if job.status.lower() == 'running' or job.status.lower() == 'pending':
-		        <h6>Kill Job</h6>
-                <a href="#" title="Kill this job" onclick="$('#kill-job').submit()">Kill this job</a>
-                <form id="kill-job" action="${url('jobbrowser.views.kill_job', jobid=job.jobId)}?next=${request.get_full_path()|urlencode}" method="POST"></form>
-		        % endif
-
-
-				<h6>Output</h6>
-				<%
-		            output_dir = job.conf_keys.get('mapredOutputDir', "")
-		            location_url = location_to_url(request, output_dir)
-		            basename = os.path.basename(output_dir)
-		            dir_name = basename.split('/')[-1]
-		          %>
-		          % if location_url != None:
-		            <a href="${location_url}" title="${output_dir}">${dir_name}</a>
-		          % else:
-		            ${dir_name}
-		          % endif
-			</div>
-		</div>
-		<div class="span10">
-			<ul class="nav nav-tabs">
-				<li class="active"><a href="#tasks" data-toggle="tab">Tasks</a></li>
-				<li><a href="#metadata" data-toggle="tab">Metadata</a></li>
-				<li><a href="#counters" data-toggle="tab">Counters</a></li>
-			</ul>
+    <h1>Job: ${job.jobId} - Job Browser</h1>
+    <div class="row-fluid">
+        <div class="span2">
+            <div class="well sidebar-nav">
+                <ul class="nav nav-list">
+                    <li class="nav-header">Job ID</li>
+                    <li>${job.jobId}</li>
+                    <li class="nav-header">User</li>
+                    <li>${job.user}</li>
+                    <li class="nav-header">Status</li>
+                    <li>
+                            % if job.status.lower() == 'running' or job.status.lower() == 'pending':
+                                <span class="label label-warning">${job.status.lower()}</span>
+                            % elif job.status.lower() == 'succeeded':
+                                <span class="label label-success">${job.status.lower()}</span>
+                            % else:
+                                <span class="label">${job.status.lower()}</span>
+                            % endif
+                    </li>
+                    % if job.status.lower() == 'running' or job.status.lower() == 'pending':
+                            <li class="nav-header">Kill Job</li>
+                            <li><a href="#" title="Kill this job" onclick="$('#kill-job').submit()">Kill this job</a>
+                                <form id="kill-job" action="${url('jobbrowser.views.kill_job', jobid=job.jobId)}?next=${request.get_full_path()|urlencode}" method="POST"></form></li>
+                    % endif
+                    <li class="nav-header">Output</li>
+                    <li>
+                        <%
+                            output_dir = job.conf_keys.get('mapredOutputDir', "")
+                            location_url = location_to_url(request, output_dir)
+                            basename = os.path.basename(output_dir)
+                            dir_name = basename.split('/')[-1]
+                        %>
+                        % if location_url != None:
+                                <a href="${location_url}" title="${output_dir}">${dir_name}</a>
+                        % else:
+                            ${dir_name}
+                        % endif
+                    </li>
+                </ul>
+            </div>
+        </div>
+        <div class="span10">
+            <ul class="nav nav-tabs">
+                <li class="active"><a href="#tasks" data-toggle="tab">Tasks</a></li>
+                <li><a href="#metadata" data-toggle="tab">Metadata</a></li>
+                <li><a href="#counters" data-toggle="tab">Counters</a></li>
+            </ul>
 
-			<div class="tab-content">
-				<div class="tab-pane active" id="tasks">
-					<dl>
-		                <dt>Maps:</dt>
-		                <dd>${comps.mr_graph_maps(job)}</dd>
-		                <dt>Reduces:</dt>
-		                <dd>${comps.mr_graph_reduces(job, right_border=True)}</dd>
-		              </dl>
-		            %if failed_tasks:
-		            <div>
-		              <h3>
-		                <a href="${url('jobbrowser.views.tasks', jobid=job.jobId)}?taskstate=failed">View Failed Tasks &raquo;</a>
-		                Failed Tasks
-		              </h3>
-		              <div class="jt_task_list_container">
-		                ${task_table(failed_tasks)}
-		              </div>
-		            </div>
-		            %endif
-		            <div>
-						<a style="float:right;margin-right:10px" href="${url('jobbrowser.views.tasks', jobid=job.jobId)}">View All Tasks &raquo;</a>
-		              <h3>
-		                Recent Tasks
-		              </h3>
-		              <div class="jt_task_list_container">
-		                ${task_table(recent_tasks)}
-		              </div>
-		            </div>
+            <div class="tab-content">
+                <div class="tab-pane active" id="tasks">
+                    <strong>Maps:</strong> ${comps.mr_graph_maps(job)}
+                    <strong>Reduces:</strong> ${comps.mr_graph_reduces(job)}
+                    %if failed_tasks:
+                            <div>
+                                <h3>
+                                    <a href="${url('jobbrowser.views.tasks', jobid=job.jobId)}?taskstate=failed">View Failed Tasks &raquo;</a>
+                                    Failed Tasks
+                                </h3>
+                                <div>
+                                ${task_table(failed_tasks)}
+                                </div>
+                            </div>
+                    %endif
+                    <div>
+                        <a style="float:right;margin-right:10px" href="${url('jobbrowser.views.tasks', jobid=job.jobId)}">View All Tasks &raquo;</a>
+                        <h3>
+                            Recent Tasks
+                        </h3>
+                        <div>
+                            ${task_table(recent_tasks)}
+                        </div>
+                    </div>
 
-				</div>
-				<div id="metadata" class="tab-pane">
-					<div class="well hueWell">
-						<form class="form-search">
-							Filter: <input id="metadataFilter" class="input-xlarge search-query" placeholder="Text Filter">
-						</form>
-					</div>
-					 <table id="metadataTable" class="table table-striped table-condensed">
-			              <thead>
-			                <th>Name</th>
-			                <th>Value</th>
-			              </thead>
-			              <tbody>
-			                <tr>
-			                  <td>ID</td>
-			                  <td>${job.jobId}</td>
-			                </tr>
-			                <tr>
-			                  <td>User</td>
-			                  <td>${job.user}</td>
-			                </tr>
-			                <tr>
-			                  <td>Maps</td>
-			                  <td>${job.finishedMaps} of ${job.desiredMaps}</td>
-			                </tr>
-			                <tr>
-			                  <td>Reduces</td>
-			                  <td>${job.finishedReduces} of ${job.desiredReduces}</td>
-			                </tr>
-			                <tr>
-			                  <td>Started</td>
-			                  <td>${job.startTimeFormatted}</td>
-			                </tr>
-			                <tr>
-			                  <td>Ended</td>
-			                  <td>${job.finishTimeFormatted}</td>
-			                </tr>
-			                <tr>
-			                  <td>Duration</td>
-			                  <td>${job.duration}</td>
-			                </tr>
-			                <tr>
-			                  <td>Status</td>
-			                  <td>${job.status}</td>
-			                </tr>
-			                ${rows_for_conf_vars(job.conf_keys)}
+                </div>
+                <div id="metadata" class="tab-pane">
+                    <div class="well hueWell">
+                        <form class="form-search">
+                            Filter: <input id="metadataFilter" class="input-xlarge search-query" placeholder="Text Filter">
+                        </form>
+                    </div>
+                    <table id="metadataTable" class="table table-striped table-condensed">
+                        <thead>
+                        <th>Name</th>
+                        <th>Value</th>
+                        </thead>
+                        <tbody>
+                        <tr>
+                            <td>ID</td>
+                            <td>${job.jobId}</td>
+                        </tr>
+                        <tr>
+                            <td>User</td>
+                            <td>${job.user}</td>
+                        </tr>
+                        <tr>
+                            <td>Maps</td>
+                            <td>${job.finishedMaps} of ${job.desiredMaps}</td>
+                        </tr>
+                        <tr>
+                            <td>Reduces</td>
+                            <td>${job.finishedReduces} of ${job.desiredReduces}</td>
+                        </tr>
+                        <tr>
+                            <td>Started</td>
+                            <td>${job.startTimeFormatted}</td>
+                        </tr>
+                        <tr>
+                            <td>Ended</td>
+                            <td>${job.finishTimeFormatted}</td>
+                        </tr>
+                        <tr>
+                            <td>Duration</td>
+                            <td>${job.duration}</td>
+                        </tr>
+                        <tr>
+                            <td>Status</td>
+                            <td>${job.status}</td>
+                        </tr>
+                            ${rows_for_conf_vars(job.conf_keys)}
 
-			              </tbody>
-			            </table>
-					  <h3>Raw configuration:</h3>
-					 <table id="rawConfigurationTable" class="table table-striped table-condensed">
-			              <thead>
-			                <th>Name</th>
-			                <th>Value</th>
-			              </thead>
-			              <tbody>
+                        </tbody>
+                    </table>
+                    <h3>Raw configuration:</h3>
+                    <table id="rawConfigurationTable" class="table table-striped table-condensed">
+                        <thead>
+                        <th>Name</th>
+                        <th>Value</th>
+                        </thead>
+                        <tbody>
 
-			              % for key, value in sorted(job.full_job_conf.items()):
-			                <tr>
-			                  <td width="20%">${key}</td>
-			                  <td>
-								<div class="wordbreak">
-									${value}
-								</div>
-							  </td>
-			                </tr>
-			              % endfor
-			              </tbody>
-			            </table>
+                                % for key, value in sorted(job.full_job_conf.items()):
+                                <tr>
+                                    <td width="20%">${key}</td>
+                                    <td>
+                                        <div class="wordbreak">
+                                        ${value}
+                                        </div>
+                                    </td>
+                                </tr>
+                                % endfor
+                        </tbody>
+                    </table>
 
-				</div>
-				<div id="counters" class="tab-pane">
-					${comps.job_counters(job.counters)}
-				</div>
-			</div>
-		</div>
-	</div>
+                </div>
+                <div id="counters" class="tab-pane">
+                    ${comps.job_counters(job.counters)}
+                </div>
+            </div>
+        </div>
+    </div>
 </div>
 
 
 
-		<script type="text/javascript" charset="utf-8">
-			$(document).ready(function(){
-				$(".taskTable").dataTable({
-					"bPaginate": false,
-				    "bLengthChange": false,
-					"bInfo": false,
-					"bAutoWidth": false,
-					"aoColumns": [
-						{ "sWidth": "40%" },
-						{ "sWidth": "40%" },
-						{ "sWidth": "20%" }
-					]
-				});
-				var _metadataTable = $("#metadataTable").dataTable({
-					"bPaginate": false,
-				    "bLengthChange": false,
-					"bInfo": false,
-					"bAutoWidth": false,
-					"aoColumns": [
-						{ "sWidth": "30%" },
-						{ "sWidth": "70%" }
-					]
-				});
-				var _rawConfigurationTable = $("#rawConfigurationTable").dataTable({
-					"bPaginate": false,
-				    "bLengthChange": false,
-					"bInfo": false,
-					"bAutoWidth": false,
-					"aoColumns": [
-						{ "sWidth": "30%" },
-						{ "sWidth": "70%" }
-					]
-				});
+<script type="text/javascript" charset="utf-8">
+    $(document).ready(function(){
+        $(".taskTable").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bAutoWidth": false,
+            "aoColumns": [
+                { "sWidth": "40%" },
+                { "sWidth": "40%" },
+                { "sWidth": "20%" }
+            ]
+        });
+        var _metadataTable = $("#metadataTable").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bAutoWidth": false,
+            "aoColumns": [
+                { "sWidth": "30%" },
+                { "sWidth": "70%" }
+            ]
+        });
+        var _rawConfigurationTable = $("#rawConfigurationTable").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bAutoWidth": false,
+            "aoColumns": [
+                { "sWidth": "30%" },
+                { "sWidth": "70%" }
+            ]
+        });
 
-				$("#metadataFilter").keydown(function(){
-					_metadataTable.fnFilter($(this).val());
-					_rawConfigurationTable.fnFilter($(this).val());
-				});
+        $("#metadataFilter").keydown(function(){
+            _metadataTable.fnFilter($(this).val());
+            _rawConfigurationTable.fnFilter($(this).val());
+        });
 
-				$(".jobCountersTable").dataTable({
-					"bPaginate": false,
-				    "bLengthChange": false,
-					"bInfo": false,
-					"bAutoWidth": false,
-					"aoColumns": [
-						{ "sWidth": "40%" },
-						{ "sWidth": "20%" },
-						{ "sWidth": "20%" },
-						{ "sWidth": "20%" }
-					]
-				});
+        $(".jobCountersTable").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bAutoWidth": false,
+            "aoColumns": [
+                { "sWidth": "40%" },
+                { "sWidth": "20%" },
+                { "sWidth": "20%" },
+                { "sWidth": "20%" }
+            ]
+        });
 
-				$(".dataTables_wrapper").css("min-height","0");
-				$(".dataTables_filter").hide();
+        $(".dataTables_wrapper").css("min-height","0");
+        $(".dataTables_filter").hide();
 
-			});
-		</script>
+    });
+</script>
 
 
-${commonfooter()}
+${commonfooter()}

+ 61 - 72
apps/jobbrowser/src/jobbrowser/templates/jobbrowser_components.mako

@@ -15,86 +15,75 @@
 ## limitations under the License.
 
 <%def name="task_counters(counters)">
-<%
-  from jobbrowser.views import format_counter_name
-%>
-	% for group in counters.groups:
-		<h3>${format_counter_name(group.displayName)}</h3>
-	    <table class="taskCountersTable table table-striped table-condensed">
-	      <thead>
-	         <tr>
-	           <th>Counter Name</th>
-	           <th>Value</th>
-	        </tr>
-	      </thead>
-	      <tbody>
-	      % for name, counter in sorted(group.counters.iteritems()):
-	       <tr>
-	          <td class="jt_counter_display_name">${format_counter_name(counter.displayName)}</td>
-	          <td class="jt_counter_total">${counter.value}</td>
-	        </tr>
-	      % endfor
-      </tbody>
-     </table>
-      % endfor
+    <%
+        from jobbrowser.views import format_counter_name
+    %>
+    % for group in counters.groups:
+        <h3>${format_counter_name(group.displayName)}</h3>
+        <table class="taskCountersTable table table-striped table-condensed">
+            <thead>
+            <tr>
+                <th>Counter Name</th>
+                <th>Value</th>
+            </tr>
+            </thead>
+        <tbody>
+            % for name, counter in sorted(group.counters.iteritems()):
+            <tr>
+                <td>${format_counter_name(counter.displayName)}</td>
+                <td>${counter.value}</td>
+            </tr>
+            % endfor
+        </tbody>
+        </table>
+    % endfor
 </%def>
 
 <%def name="job_counters(counters)">
-<%
-  from jobbrowser.views import format_counter_name
-%>
-
-	% for group in counters.itervalues():
-      <h3>${format_counter_name(group['displayName'])}</h3>
-	  <table class="jobCountersTable table table-striped table-condensed">
-      <thead>
-         <tr>
-           <th>Name</th>
-           <th>Maps Total</th>
-           <th>Reduces Total</th>
-           <th>Total</th>
-        </tr>
-      </thead>
-      <tbody>
-		 % for name, counter in sorted(group['counters'].iteritems()):
-	      <%
-	        map_count = counter.get('map', 0)
-	        reduce_count = counter.get('reduce', 0)
-	        job_count = counter.get('job', 0)
-	      %>
-	       <tr>
-	          <td>${format_counter_name(counter.get('displayName', 'n/a'))}</td>
-	          <td>${map_count}</td>
-	          <td>${reduce_count}</td>
-	          <td>${map_count + reduce_count + job_count}</td>
-	        </tr>
-	      % endfor
-			</tbody>
-	     </table>
-	% endfor
-
-
-
+    <%
+        from jobbrowser.views import format_counter_name
+    %>
+    % for group in counters.itervalues():
+        <h3>${format_counter_name(group['displayName'])}</h3>
+        <table class="jobCountersTable table table-striped table-condensed">
+            <thead>
+            <tr>
+                <th>Name</th>
+                <th>Maps Total</th>
+                <th>Reduces Total</th>
+                <th>Total</th>
+            </tr>
+            </thead>
+        <tbody>
+            % for name, counter in sorted(group['counters'].iteritems()):
+            <%
+                map_count = counter.get('map', 0)
+                reduce_count = counter.get('reduce', 0)
+                job_count = counter.get('job', 0)
+            %>
+            <tr>
+                <td>${format_counter_name(counter.get('displayName', 'n/a'))}</td>
+                <td>${map_count}</td>
+                <td>${reduce_count}</td>
+                <td>${map_count + reduce_count + job_count}</td>
+            </tr>
+            % endfor
+        </tbody>
+        </table>
+    % endfor
 </%def>
 
 <%def name="mr_graph(job)">
-  <div class="jt_mr_display">
-    ${mr_graph_maps(job)}
-    ${mr_graph_reduces(job)}
-  </div>
+    <div>
+        ${mr_graph_maps(job)}
+        ${mr_graph_reduces(job)}
+    </div>
 </%def>
 
 <%def name="mr_graph_maps(job)">
-  <div class="jt_maps">
-    <span class="jt_maps_complete" style="width: ${job.maps_percent_complete}%;">${job.finishedMaps} / ${job.desiredMaps}</span>
-    <span class="jt_white_border"></span>
-  </div>
+    <div class="bar">${job.finishedMaps} / ${job.desiredMaps}</div>
 </%def>
-<%def name="mr_graph_reduces(job, right_border=False)">
-  <div class="jt_reduces">
-    <span class="jt_reduces_complete" style="width: ${job.reduces_percent_complete}%;">${job.finishedReduces} / ${job.desiredReduces}</span>
-    % if right_border:
-      <span class="jt_white_border"></span>
-    % endif
-  </div>
+
+<%def name="mr_graph_reduces(job)">
+    <div class="bar">${job.finishedReduces} / ${job.desiredReduces}</div>
 </%def>

+ 87 - 89
apps/jobbrowser/src/jobbrowser/templates/jobs.mako

@@ -20,35 +20,34 @@
   from desktop.views import commonheader, commonfooter
 %>
 <%namespace name="comps" file="jobbrowser_components.mako" />
+
 <%def name="get_state(option, state)">
 %   if option == state:
       selected="true"
 %   endif
 </%def>
 
-
-
 % if len(jobs) > 0 or filtered:
 ${commonheader("Job Browser", "jobbrowser")}
 <div class="container-fluid">
 <h1>Job Browser</h1>
 <div class="well hueWell">
-	<form action="/jobbrowser/jobs" method="GET">
-		<b>Filter jobs:</b>
+    <form action="/jobbrowser/jobs" method="GET">
+        <b>Filter jobs:</b>
 
-				<select name="state" class="submitter">
-					<option value="all" ${get_state('all', state_filter)}>All States</option>
-					<option value="running" ${get_state('running', state_filter)}>Running</option>
-					<option value="completed" ${get_state('completed', state_filter)}>Completed</option>
-					<option value="failed" ${get_state('failed', state_filter)}>Failed</option>
-					<option value="killed" ${get_state('killed', state_filter)}>Killed</option>
-				</select>
+                <select name="state" class="submitter">
+                    <option value="all" ${get_state('all', state_filter)}>All States</option>
+                    <option value="running" ${get_state('running', state_filter)}>Running</option>
+                    <option value="completed" ${get_state('completed', state_filter)}>Completed</option>
+                    <option value="failed" ${get_state('failed', state_filter)}>Failed</option>
+                    <option value="killed" ${get_state('killed', state_filter)}>Killed</option>
+                </select>
 
-				<input type="text" name="user" title="User Name Filter" value="${user_filter}" placeholder="User Name Filter" class="submitter"/>
+                <input type="text" name="user" title="User Name Filter" value="${user_filter}" placeholder="User Name Filter" class="submitter"/>
 
-				<input type="text" name="text" title="Text Filter" value="${text_filter}" placeholder="Text Filter" class="submitter"/>
+                <input type="text" name="text" title="Text Filter" value="${text_filter}" placeholder="Text Filter" class="submitter"/>
 
-	</form>
+    </form>
 </div>
 
 
@@ -56,38 +55,38 @@ ${commonheader("Job Browser", "jobbrowser")}
 <p>There were no jobs that match your search criteria.</p>
 % else:
 <table class="datatables table table-striped table-condensed">
-	<thead>
-		<tr>
-			<th>Name / Id</th>
-			<th>Status</th>
-			<th>User</th>
-			<th>Maps/Reduces</th>
-			<th>Queue</th>
-			<th>Priority</th>
-			<th>Duration</th>
-			<th>Date</th>
-			<th></th>
-		</tr>
-	</thead>
-	<tbody>
-		% for job in jobs:
-		<tr>
-			<td>${job.jobName}
-				<div class="jobbrowser_jobid_short">${job.jobId_short}</div>
-			</td>
-			<td>
-				<a href="${url('jobbrowser.views.jobs')}?${get_state_link(request, 'state', job.status.lower())}" title="Show only ${job.status.lower()} jobs">${job.status.lower()}</a>
-			</td>
-			<td>
-				<a href="${url('jobbrowser.views.jobs')}?${get_state_link(request, 'user', job.user.lower())}" title="Show only ${job.user.lower()} jobs">${job.user}</a>
-			</td>
-			<td class="jt_mrs">
-				${comps.mr_graph(job)}
-			</td>
-			<td>${job.queueName}</td>
-			<td>${job.priority.lower()}</td>
-			<td>${job.durationFormatted}</td>
-			<td>${job.startTimeFormatted}</td>
+    <thead>
+        <tr>
+            <th>Name / Id</th>
+            <th>Status</th>
+            <th>User</th>
+            <th>Maps</th>
+            <th>Reduces</th>
+            <th>Queue</th>
+            <th>Priority</th>
+            <th>Duration</th>
+            <th>Date</th>
+            <th></th>
+        </tr>
+    </thead>
+    <tbody>
+        % for job in jobs:
+        <tr>
+            <td>${job.jobName}
+                <div class="jobbrowser_jobid_short">${job.jobId_short}</div>
+            </td>
+            <td>
+                <a href="${url('jobbrowser.views.jobs')}?${get_state_link(request, 'state', job.status.lower())}" title="Show only ${job.status.lower()} jobs">${job.status.lower()}</a>
+            </td>
+            <td>
+                <a href="${url('jobbrowser.views.jobs')}?${get_state_link(request, 'user', job.user.lower())}" title="Show only ${job.user.lower()} jobs">${job.user}</a>
+            </td>
+            <td><span alt="${job.maps_percent_complete}">${comps.mr_graph_maps(job)}</span></td>
+            <td><span alt="${job.reduces_percent_complete}">${comps.mr_graph_reduces(job)}</span></td>
+            <td>${job.queueName}</td>
+            <td>${job.priority.lower()}</td>
+            <td><span alt="${job.finishTimeMs-job.startTimeMs}">${job.durationFormatted}</span></td>
+            <td><span alt="${job.startTimeMs}">${job.startTimeFormatted}</span></td>
             <td>
                 <a href="${url('jobbrowser.views.single_job', jobid=job.jobId)}" title="View this job" data-row-selector="true">View</a>
                 % if job.status.lower() == 'running' or job.status.lower() == 'pending':
@@ -97,51 +96,50 @@ ${commonheader("Job Browser", "jobbrowser")}
                 % endif
                 % endif
             </td>
-			</tr>
-			% endfor
-		</tbody>
-	</table>
-	% endif
-
-
+            </tr>
+            % endfor
+        </tbody>
+    </table>
+    % endif
 
-	% else:
-	${commonheader("Job Browser", "jobbrowser")}
-	<div class="container-fluid">
-	<h1>Welcome to the Job Browser</h1>
-	<div>
-		<p>There aren't any jobs running. Let's fix that.</p>
-		% if appmanager.get_desktop_module('jobsub') is not None:
-		<a href="/jobsub/">Launch the Job Designer</a><br/>
-		% endif
-		% if appmanager.get_desktop_module('beeswax') is not None:
-		<a href="/beeswax/">Launch Beeswax</a><br/>
-		% endif
-	</div>
-	% endif
+    % else:
+    ${commonheader("Job Browser", "jobbrowser")}
+    <div class="container-fluid">
+    <h1>Welcome to the Job Browser</h1>
+    <div>
+        <p>There aren't any jobs running. Let's fix that.</p>
+        % if appmanager.get_desktop_module('jobsub') is not None:
+        <a href="/jobsub/">Launch the Job Designer</a><br/>
+        % endif
+        % if appmanager.get_desktop_module('beeswax') is not None:
+        <a href="/beeswax/">Launch Beeswax</a><br/>
+        % endif
+    </div>
+    % endif
 </div>
-	<script type="text/javascript" charset="utf-8">
-	$(document).ready(function(){
-		$(".datatables").dataTable({
-			"bPaginate": false,
-			"bLengthChange": false,
-			"bFilter": false,
-			"bInfo": false,
-			"aoColumns": [
-			null,
-			null,
-			null,
-			null,
-			null,
-			null,
-			null,
-			null,
-			{"bSortable":false}
-			]
-		});
-		$("a[data-row-selector='true']").jHueRowSelector();
-	});
-	</script>
 
+<script type="text/javascript" charset="utf-8">
+    $(document).ready(function(){
+        $(".datatables").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bFilter": false,
+            "bInfo": false,
+            "aoColumns": [
+                null,
+                null,
+                null,
+                { "sType": "alt-numeric" },
+                { "sType": "alt-numeric" },
+                null,
+                null,
+                { "sType": "alt-numeric" },
+                { "sType": "alt-numeric" },
+                {"bSortable":false}
+            ]
+        });
+        $("a[data-row-selector='true']").jHueRowSelector();
+    });
+</script>
 
 ${commonfooter()}

+ 6 - 6
apps/jobbrowser/src/jobbrowser/templates/jobs_dock_info.mako

@@ -14,10 +14,10 @@
 ## See the License for the specific language governing permissions and
 ## limitations under the License.
 % if jobs.get('all') > 0:
-  jobs:
-  % for state in ['running', 'failed', 'completed']:
-    % if jobs.get(state) > 0:
-      <a href="${url('jobbrowser.views.jobs')}?user=${user.username}&state=${state}" target="JobBrowser" class="hue-dock-status-${state}">${jobs.get(state)} ${state}</a>
-    % endif
-  % endfor
+    jobs:
+    % for state in ['running', 'failed', 'completed']:
+        % if jobs.get(state) > 0:
+            <a href="${url('jobbrowser.views.jobs')}?user=${user.username}&state=${state}">${jobs.get(state)} ${state}</a>
+        % endif
+    % endfor
 % endif

+ 154 - 153
apps/jobbrowser/src/jobbrowser/templates/task.mako

@@ -21,165 +21,166 @@
 ${commonheader("Job Task: " + task.taskId + "- Job Browser", "jobbrowser")}
 
 <div class="container-fluid">
-	<h1>Job Task: ${task.taskId} - Job Browser</h1>
-	<div class="row-fluid">
-		<div class="span2">
-			<div class="well sidebar-nav">
-				<h6>Task ID</h6>
-				${task.taskId_short}
+    <h1>Job Task: ${task.taskId} - Job Browser</h1>
+    <div class="row-fluid">
+        <div class="span2">
+            <div class="well sidebar-nav">
+                <ul class="nav nav-list">
+                    <li class="nav-header">Task ID</li>
+                    <li>${task.taskId_short}</li>
+                    <li class="nav-header">Job</li>
+                    <li><a href="${url('jobbrowser.views.single_job', jobid=joblnk.jobId)}" title="View this job">${joblnk.jobId_short}</a></li>
+                    <li class="nav-header">Status</li>
+                    <li>
+                            % if task.state.lower() == 'running' or task.state.lower() == 'pending':
+                                <span class="label label-warning">${task.state.lower()}</span>
+                            % elif task.state.lower() == 'succeeded':
+                                <span class="label label-success">${task.state.lower()}</span>
+                            % else:
+                                <span class="label">${task.state.lower()}</span>
+                            % endif
+                    </li>
+                </ul>
+            </div>
+        </div>
+        <div class="span10">
+            <ul class="nav nav-tabs">
+                <li class="active"><a href="#attempts" data-toggle="tab">Attempts</a></li>
+                <li><a href="#metadata" data-toggle="tab">Metadata</a></li>
+                <li><a href="#counters" data-toggle="tab">Counters</a></li>
+            </ul>
 
-				<h6>Job</h6>
-				<a href="${url('jobbrowser.views.single_job', jobid=joblnk.jobId)}" class="frame_tip jt_view" title="View this job">${joblnk.jobId_short}</a>
-
-				<h6>Status</h6>
-				% if task.state.lower() == 'running' or task.state.lower() == 'pending':
-					<span class="label label-warning">${task.state.lower()}</span>
-				% elif task.state.lower() == 'succeeded':
-					<span class="label label-success">${task.state.lower()}</span>
-				% else:
-					<span class="label">${task.state.lower()}</span>
-				% endif
-			</div>
-		</div>
-		<div class="span10">
-			<ul class="nav nav-tabs">
-				<li class="active"><a href="#attempts" data-toggle="tab">Attempts</a></li>
-				<li><a href="#metadata" data-toggle="tab">Metadata</a></li>
-				<li><a href="#counters" data-toggle="tab">Counters</a></li>
-			</ul>
-
-			<div class="tab-content">
-				<div class="tab-pane active" id="attempts">
-					<table id="attemptsTable" class="table table-striped table-condensed">
-		              <thead>
-		                <tr>
-		                 <th>Attempt ID</th>
-		                 <th>Progress</th>
-		                 <th>State</th>
-		                 <th>Task Tracker</th>
-		                 <th>Start Time</th>
-		                 <th>End Time</th>
-		                 <th>Output Size</th>
-		                 <th>Phase</th>
-		                 <th>Shuffle Finish</th>
-		                 <th>Sort Finish</th>
-		                 <th>Map Finish</th>
-		                 <th>View</th>
-		                </tr>
-		              </thead>
-		              <tbody>
-		                % for attempt in task.attempts:
-		                 <tr data-dblclick-delegate="{'dblclick_loads':'.view_attempt'}">
-		                   <td>${attempt.attemptId_short}</td>
-		                   <td>${"%d" % (attempt.progress * 100)}%</td>
-		                   <td><span class="status_link ${attempt.state}">${attempt.state}</span></td>
-		                   <td><a href="/jobbrowser/trackers/${attempt.taskTrackerId}" class="task_tracker_link">${attempt.taskTrackerId}</a></td>
-		                   <td>${attempt.startTimeFormatted}</td>
-		                   <td>${attempt.finishTimeFormatted}</td>
-		                   <td>${attempt.outputSize}</td>
-		                   <td>${attempt.phase}</td>
-		                   <td>${attempt.shuffleFinishTimeFormatted}</td>
-		                   <td>${attempt.sortFinishTimeFormatted}</td>
-		                   <td>${attempt.mapFinishTimeFormatted}</td>
-		                   <td><a class="frame_tip jtask_view jt_slide_right view_attempt" title="View this attempt"
-		                          href="${ url('jobbrowser.views.single_task_attempt', jobid=joblnk.jobId, taskid=task.taskId, attemptid=attempt.attemptId) }">View</a></td>
-		                 </tr>
-		                % endfor
-		              </tbody>
-		            </table>
-				</div>
-				<div id="metadata" class="tab-pane">
-					<table id="metadataTable" class="table table-striped table-condensed">
-		              <thead>
-		                <th>Name</th>
-		                <th>Value</th>
-		              </thead>
-		              <tbody>
-		                <tr>
-		                  <td>Task id</td>
-		                  <td>${task.taskId}</td>
-		                </tr>
-		                <tr>
-		                  <td>Type</td>
-		                  <td>${task.taskType}</td>
-		                </tr>
-		                <tr>
-		                  <td>JobId</td>
-		                  <td><a href="${url('jobbrowser.views.single_job', jobid=joblnk.jobId)}" class="frame_tip jt_view" title="View this job">${joblnk.jobId}</a></td>
-		                </tr>
-		                <tr>
-		                  <td>State</td>
-		                  <td>${task.state}</td>
-		                </tr>
-		                <tr>
-		                  <td>Status</td>
-		                  <td>${task.mostRecentState}</td>
-		                </tr>
-		                <tr>
-		                  <td>Start Time</td>
-		                  <td>${task.startTimeFormatted}</td>
-		                </tr>
-		                <tr>
-		                  <td>Execution Start Time</td>
-		                  <td>${task.execStartTimeFormatted}</td>
-		                </tr>
-		                <tr>
-		                  <td>Execution Finish Time</td>
-		                  <td>${task.execFinishTimeFormatted}</td>
-		                </tr>
-		                <tr>
-		                  <td>Progress</td>
-		                  <td>${"%d" % (task.progress * 100)}%</td>
-		                </tr>
-		              </tbody>
-		            </table>
-				</div>
-				<div id="counters" class="tab-pane">
-					${comps.task_counters(task.counters)}
-				</div>
-			</div>
-		</div>
-	</div>
+            <div class="tab-content">
+                <div class="tab-pane active" id="attempts">
+                    <table id="attemptsTable" class="table table-striped table-condensed">
+                        <thead>
+                        <tr>
+                            <th>Attempt ID</th>
+                            <th>Progress</th>
+                            <th>State</th>
+                            <th>Task Tracker</th>
+                            <th>Start Time</th>
+                            <th>End Time</th>
+                            <th>Output Size</th>
+                            <th>Phase</th>
+                            <th>Shuffle Finish</th>
+                            <th>Sort Finish</th>
+                            <th>Map Finish</th>
+                            <th>View</th>
+                        </tr>
+                        </thead>
+                        <tbody>
+                                % for attempt in task.attempts:
+                                <tr>
+                                    <td>${attempt.attemptId_short}</td>
+                                    <td>${"%d" % (attempt.progress * 100)}%</td>
+                                    <td><span class="status_link ${attempt.state}">${attempt.state}</span></td>
+                                    <td><a href="/jobbrowser/trackers/${attempt.taskTrackerId}" class="task_tracker_link">${attempt.taskTrackerId}</a></td>
+                                    <td>${attempt.startTimeFormatted}</td>
+                                    <td>${attempt.finishTimeFormatted}</td>
+                                    <td>${attempt.outputSize}</td>
+                                    <td>${attempt.phase}</td>
+                                    <td>${attempt.shuffleFinishTimeFormatted}</td>
+                                    <td>${attempt.sortFinishTimeFormatted}</td>
+                                    <td>${attempt.mapFinishTimeFormatted}</td>
+                                    <td><a title="View this attempt"
+                                           href="${ url('jobbrowser.views.single_task_attempt', jobid=joblnk.jobId, taskid=task.taskId, attemptid=attempt.attemptId) }" data-row-selector="true">View</a></td>
+                                </tr>
+                                % endfor
+                        </tbody>
+                    </table>
+                </div>
+                <div id="metadata" class="tab-pane">
+                    <table id="metadataTable" class="table table-striped table-condensed">
+                        <thead>
+                        <th>Name</th>
+                        <th>Value</th>
+                        </thead>
+                        <tbody>
+                        <tr>
+                            <td>Task id</td>
+                            <td>${task.taskId}</td>
+                        </tr>
+                        <tr>
+                            <td>Type</td>
+                            <td>${task.taskType}</td>
+                        </tr>
+                        <tr>
+                            <td>JobId</td>
+                            <td><a href="${url('jobbrowser.views.single_job', jobid=joblnk.jobId)}" title="View this job">${joblnk.jobId}</a></td>
+                        </tr>
+                        <tr>
+                            <td>State</td>
+                            <td>${task.state}</td>
+                        </tr>
+                        <tr>
+                            <td>Status</td>
+                            <td>${task.mostRecentState}</td>
+                        </tr>
+                        <tr>
+                            <td>Start Time</td>
+                            <td>${task.startTimeFormatted}</td>
+                        </tr>
+                        <tr>
+                            <td>Execution Start Time</td>
+                            <td>${task.execStartTimeFormatted}</td>
+                        </tr>
+                        <tr>
+                            <td>Execution Finish Time</td>
+                            <td>${task.execFinishTimeFormatted}</td>
+                        </tr>
+                        <tr>
+                            <td>Progress</td>
+                            <td>${"%d" % (task.progress * 100)}%</td>
+                        </tr>
+                        </tbody>
+                    </table>
+                </div>
+                <div id="counters" class="tab-pane">
+                    ${comps.task_counters(task.counters)}
+                </div>
+            </div>
+        </div>
+    </div>
 </div>
 
 
 
-		<script type="text/javascript" charset="utf-8">
-			$(document).ready(function(){
-				$("#attemptsTable").dataTable({
-					"bPaginate": false,
-				    "bLengthChange": false,
-					"bInfo": false,
-					"bFilter": false
-				});
-				$("#metadataTable").dataTable({
-					"bPaginate": false,
-				    "bLengthChange": false,
-					"bInfo": false,
-					"bAutoWidth": false,
-					"bFilter": false,
-					"aoColumns": [
-						{ "sWidth": "30%" },
-						{ "sWidth": "70%" }
-					]
-				});
-
-				$(".taskCountersTable").dataTable({
-					"bPaginate": false,
-				    "bLengthChange": false,
-					"bInfo": false,
-					"bAutoWidth": false,
-					"aoColumns": [
-						{ "sWidth": "30%" },
-						{ "sWidth": "70%" }
-					]
-				});
+<script type="text/javascript" charset="utf-8">
+    $(document).ready(function(){
+        $("#attemptsTable").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bFilter": false
+        });
+        $("#metadataTable").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bAutoWidth": false,
+            "bFilter": false,
+            "aoColumns": [
+                { "sWidth": "30%" },
+                { "sWidth": "70%" }
+            ]
+        });
 
-				$(".dataTables_wrapper").css("min-height","0");
-				$(".dataTables_filter").hide();
+        $(".taskCountersTable").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bInfo": false,
+            "bFilter": false,
+            "bAutoWidth": false,
+            "aoColumns": [
+                { "sWidth": "30%" },
+                { "sWidth": "70%" }
+            ]
+        });
 
-			});
-		</script>
+        $("a[data-row-selector='true']").jHueRowSelector();
+    });
+</script>
 
 
-    ${commonfooter()}
+${commonfooter()}

+ 85 - 80
apps/jobbrowser/src/jobbrowser/templates/tasks.mako

@@ -20,93 +20,98 @@
 
 <%namespace name="comps" file="jobbrowser_components.mako" />
 
-  ${commonheader("Task View: Job: " + jobid + " - Job Browser", "jobbrowser")}
-  <%def name="selected(val, state)">
-  %   if val is not None and state is not None and val in state:
-        selected="true"
-  %   endif
-  </%def>
-	<div class="container-fluid">
-	<h1>Task View: Job:  ${jobid}</h1>
-	<div class="well hueWell">
-		<form method="get" action="/jobbrowser/jobs/${jobid}/tasks">
-			<b>Filter tasks:</b>
+${commonheader("Task View: Job: " + jobid + " - Job Browser", "jobbrowser")}
 
-				<select name="taskstate" class="submitter">
-					<option value="">All states</option>
-					<option value="succeeded" ${selected('succeeded', taskstate)}>succeeded</option>
-					<option value="running" ${selected('running', taskstate)}>running</option>
-					<option value="failed" ${selected('failed', taskstate)}>failed</option>
-					<option value="killed" ${selected('killed', taskstate)}>killed</option>
-					<option value="pending" ${selected('pending', taskstate)}>pending</option>
-				</select>
+<%def name="selected(val, state)">
+    %   if val is not None and state is not None and val in state:
+        selected="true"
+    %   endif
+</%def>
+<div class="container-fluid">
+    <h1>Task View: Job:  ${jobid}</h1>
+    <div class="well hueWell">
+        <form method="get" action="/jobbrowser/jobs/${jobid}/tasks">
+            <b>Filter tasks:</b>
 
+            <select name="taskstate" class="submitter">
+                <option value="">All states</option>
+                <option value="succeeded" ${selected('succeeded', taskstate)}>succeeded</option>
+                <option value="running" ${selected('running', taskstate)}>running</option>
+                <option value="failed" ${selected('failed', taskstate)}>failed</option>
+                <option value="killed" ${selected('killed', taskstate)}>killed</option>
+                <option value="pending" ${selected('pending', taskstate)}>pending</option>
+            </select>
 
-				<select name="tasktype" class="submitter">
-					<option value="">All types</option>
-					<option value="map" ${selected('map', tasktype)}>maps</option>
-					<option value="reduce" ${selected('reduce', tasktype)}>reduces</option>
-					<option value="job_cleanup" ${selected('job_cleanup', tasktype)}>cleanups</option>
-					<option value="job_setup" ${selected('job_setup', tasktype)}>setups</option>
-				</select>
 
+            <select name="tasktype" class="submitter">
+                <option value="">All types</option>
+                <option value="map" ${selected('map', tasktype)}>maps</option>
+                <option value="reduce" ${selected('reduce', tasktype)}>reduces</option>
+                <option value="job_cleanup" ${selected('job_cleanup', tasktype)}>cleanups</option>
+                <option value="job_setup" ${selected('job_setup', tasktype)}>setups</option>
+            </select>
 
-				<input type="text" name="tasktext"  class="submitter" title="Text filter" placeholder="Text Filter"
-				% if tasktext:
-				value="${tasktext}"
-				% endif
-				/>
 
-		</form>
-	</div>
+            <input type="text" name="tasktext"  class="submitter" title="Text filter" placeholder="Text Filter"
+                % if tasktext:
+                   value="${tasktext}"
+                % endif
+                    />
+        </form>
+    </div>
 
 
-	% if len(page.object_list) == 0:
-	<p>There were no tasks that match your search criteria.</p>
-	% else:
+    % if len(page.object_list) == 0:
+         <p>There were no tasks that match your search criteria.</p>
+    % else:
+        <table class="datatables table table-striped table-condensed">
+            <thead>
+            <tr>
+                <th>Task ID</th>
+                <th>Type</th>
+                <th>Progress</th>
+                <th>Status</th>
+                <th>State</th>
+                <th>Start Time</th>
+                <th>End Time</th>
+                <th>View Attempts</th>
+            </tr>
+            </thead>
+	        <tbody>
+	            %for t in page.object_list:
+	            <tr>
+	                <td>${t.taskId_short}</td>
+	                <td>${t.taskType}</td>
+	                <td>
+	                   <div class="bar">${"%d" % (t.progress * 100)}%</div>
+	                </td>
+	                <td>
+	                    <a href="${url('jobbrowser.views.tasks', jobid=jobid)}?${get_state_link(request, 'taskstate', t.state.lower())}"
+	                       title="Show only ${t.state.lower()} tasks"
+	                       class="${t.state.lower()}">${t.state.lower()}
+	                    </a>
+	                </td>
+	                <td>${t.mostRecentState}</td>
+	                <td>${t.execStartTimeFormatted}</td>
+	                <td>${t.execFinishTimeFormatted}</td>
+	                <td><a href="/jobbrowser/jobs/${jobid}/tasks/${t.taskId}" data-row-selector="true">Attempts</a></td>
+	            </tr>
+	            %endfor
+	        </tbody>
+        </table>
+    %endif
+</div>
 
-	<table class="datatables table table-striped table-condensed">
-		<thead>
-			<tr>
-				<th>Task ID</th>
-				<th>Type</th>
-				<th>Progress</th>
-				<th>Status</th>
-				<th>State</th>
-				<th>Start Time</th>
-				<th>End Time</th>
-				<th>View Attempts</th>
-			</tr>
-		</thead>
-		<tbody>
-			%for t in page.object_list:
-			<tr data-dblclick-delegate="{'dblclick_loads':'.view_task'}">
-				<td>${t.taskId_short}</td>
-				<td>${t.taskType}</td>
-				<td>${"%d" % (t.progress * 100)}%</td>
-				<td><a href="${url('jobbrowser.views.tasks', jobid=jobid)}?${get_state_link(request, 'taskstate', t.state.lower())}"
-					title="Show only ${t.state.lower()} tasks"
-					class="${t.state.lower()}">${t.state.lower()}</a></td>
-					<td>${t.mostRecentState}</td>
-					<td>${t.execStartTimeFormatted}</td>
-					<td>${t.execFinishTimeFormatted}</td>
-					<td><a href="/jobbrowser/jobs/${jobid}/tasks/${t.taskId}" data-row-selector="true">Attempts</a></td>
-				</tr>
-				%endfor
-		</tbody>
-	</table>
-	%endif
-	</div>
-	<script type="text/javascript" charset="utf-8">
-		$(document).ready(function(){
-			$(".datatables").dataTable({
-				"bPaginate": false,
-			    "bLengthChange": false,
-			    "bFilter": false,
-				"bInfo": false,
-			});
-			$("a[data-row-selector='true']").jHueRowSelector();
-		});
-	</script>
+<script type="text/javascript" charset="utf-8">
+    $(document).ready(function(){
+        $(".datatables").dataTable({
+            "bPaginate": false,
+            "bLengthChange": false,
+            "bFilter": false,
+            "bInfo": false,
+        });
+        $("a[data-row-selector='true']").jHueRowSelector();
+    });
+</script>
 
-  ${commonfooter()}
+${commonfooter()}

+ 71 - 72
apps/jobsub/src/jobsub/templates/list_designs.mako

@@ -15,11 +15,12 @@
 ## limitations under the License.
 
 <%!
-  import cgi
-  import urllib
-  from django.template.defaultfilters import date, time
-  from desktop.lib.django_util import extract_field_data
-  from desktop.views import commonheader, commonfooter
+    import cgi
+    import urllib
+    import time as py_time
+    from django.template.defaultfilters import date, time
+    from desktop.lib.django_util import extract_field_data
+    from desktop.views import commonheader, commonfooter
 %>
 <%namespace name="layout" file="layout.mako" />
 
@@ -30,60 +31,60 @@ ${layout.menubar(section='designs')}
 
 <div class="container-fluid">
     <h1>Job Designs</h1>
-	<div class="well hueWell">
-			<div class="btn-group pull-right">
-				<a href="${ url('jobsub.views.new_design', action_type='mapreduce') }" class="btn">Create Mapreduce Design</a>
-            	<a href="${ url('jobsub.views.new_design', action_type='streaming') }" class="btn">Create Streaming Design</a>
-            	<a href="${ url('jobsub.views.new_design', action_type='java') }" class="btn">Create Java Design</a>
-				%if show_install_examples:
-					<a id="installSamplesLink" href="javascript:void(0)" data-confirmation-url="${url('jobsub.views.setup')}" class="btn" >Install Samples</a>
-				%endif
-			</div>
-
-		<form class="form-search">
-			Filter: <input id="filterInput" class="input-xlarge search-query" placeholder="Search for username, name, etc...">
-		</form>
-	</div>
+    <div class="well hueWell">
+        <div class="btn-group pull-right">
+            <a href="${ url('jobsub.views.new_design', action_type='mapreduce') }" class="btn">Create Mapreduce Design</a>
+            <a href="${ url('jobsub.views.new_design', action_type='streaming') }" class="btn">Create Streaming Design</a>
+            <a href="${ url('jobsub.views.new_design', action_type='java') }" class="btn">Create Java Design</a>
+            %if show_install_examples:
+                    <a id="installSamplesLink" href="javascript:void(0)" data-confirmation-url="${url('jobsub.views.setup')}" class="btn" >Install Samples</a>
+            %endif
+        </div>
+
+        <form class="form-search">
+            Filter: <input id="filterInput" class="input-xlarge search-query" placeholder="Search for username, name, etc...">
+        </form>
+    </div>
 
     <table id="designTable" class="table table-condensed datatables">
         <thead>
-            <tr>
-                <th>Owner</th>
-                <th>Name</th>
-                <th>Type</th>
-                <th>Description</th>
-                <th>Last Modified</th>
-                <th nowrap="nowrap">&nbsp;</th>
-            </tr>
+        <tr>
+            <th>Owner</th>
+            <th>Name</th>
+            <th>Type</th>
+            <th>Description</th>
+            <th>Last Modified</th>
+            <th nowrap="nowrap">&nbsp;</th>
+        </tr>
         </thead>
         <tbody>
-            %for design in designs:
+                %for design in designs:
                 <tr>
                     <td>${design.owner.username}</td>
                     <td>${design.name}</td>
                     <td>${design.root_action.action_type}</td>
                     <td>${design.description}</td>
-                    <td nowrap="nowrap">${date(design.last_modified)} ${time(design.last_modified).replace("p.m.","PM").replace("a.m.","AM")}</td>
-                    <td nowrap="nowrap" class="right">
-                    %if currentuser.username == design.owner.username:
-                        <a title="Submit ${design.name}" class="btn small submitConfirmation"
-                           alt="Submit ${design.name} to the cluster"
-                           href="javascript:void(0)"
-                           data-param-url="${ url('jobsub.views.get_design_params', design_id=design.id) }"
-                           data-submit-url="${ url('jobsub.views.submit_design', design_id=design.id) }">Submit</a>
-                        <a title="Edit ${design.name}" class="btn small"
-                           href="${ url('jobsub.views.edit_design', design_id=design.id) }" data-row-selector="true">Edit</a>
+                    <td nowrap="nowrap"><span alt="${py_time.mktime(design.last_modified.timetuple())}">${date(design.last_modified)} ${time(design.last_modified).replace("p.m.","PM").replace("a.m.","AM")}</span></td>
+                <td nowrap="nowrap" class="right">
+                %if currentuser.username == design.owner.username:
+                    <a title="Submit ${design.name}" class="btn small submitConfirmation"
+                       alt="Submit ${design.name} to the cluster"
+                       href="javascript:void(0)"
+                       data-param-url="${ url('jobsub.views.get_design_params', design_id=design.id) }"
+                       data-submit-url="${ url('jobsub.views.submit_design', design_id=design.id) }">Submit</a>
+                    <a title="Edit ${design.name}" class="btn small"
+                       href="${ url('jobsub.views.edit_design', design_id=design.id) }" data-row-selector="true">Edit</a>
                     %endif%
-                    %if currentuser.is_superuser or currentuser.username == design.owner.username:
+                %if currentuser.is_superuser or currentuser.username == design.owner.username:
                         <a title="Delete ${design.name}" class="btn small deleteConfirmation"
                            alt="Are you sure you want to delete ${design.name}?"
                            href="javascript:void(0)"
                            data-confirmation-url="${ url('jobsub.views.delete_design', design_id=design.id) }">Delete</a>
-                    %endif
-                        <a title="Clone ${design.name}" class="btn small" href="${ url('jobsub.views.clone_design', design_id=design.id) }">Clone</a>
-                    </td>
+                %endif
+                    <a title="Clone ${design.name}" class="btn small" href="${ url('jobsub.views.clone_design', design_id=design.id) }">Clone</a>
+                </td>
                 </tr>
-            %endfor
+                %endfor
         </tbody>
     </table>
 
@@ -91,7 +92,7 @@ ${layout.menubar(section='designs')}
 
 
 <div id="submitWf" class="modal hide fade">
-	<form id="submitWfForm" action="" method="POST">
+    <form id="submitWfForm" action="" method="POST">
         <div class="modal-header">
             <a href="#" class="close" data-dismiss="modal">&times;</a>
             <h3 id="submitWfMessage">Submit this design?</h3>
@@ -106,11 +107,11 @@ ${layout.menubar(section='designs')}
             <input id="submitBtn" type="submit" class="btn primary" value="Submit"/>
             <a href="#" class="btn secondary hideModal">Cancel</a>
         </div>
-	</form>
+    </form>
 </div>
 
 <div id="deleteWf" class="modal hide fade">
-	<form id="deleteWfForm" action="" method="POST">
+    <form id="deleteWfForm" action="" method="POST">
         <div class="modal-header">
             <a href="#" class="close" data-dismiss="modal">&times;</a>
             <h3 id="deleteWfMessage">Delete this design?</h3>
@@ -119,23 +120,23 @@ ${layout.menubar(section='designs')}
             <input type="submit" class="btn primary" value="Yes"/>
             <a href="#" class="btn secondary hideModal">No</a>
         </div>
-	</form>
+    </form>
 </div>
 
 <div id="installSamples" class="modal hide fade">
-	<form id="installSamplesForm" action="${url('jobsub.views.setup')}" method="POST">
+    <form id="installSamplesForm" action="${url('jobsub.views.setup')}" method="POST">
         <div class="modal-header">
             <a href="#" class="close" data-dismiss="modal">&times;</a>
             <h3>Install sample job designs?</h3>
         </div>
         <div class="modal-body">
-          It will take a few seconds to install.
+            It will take a few seconds to install.
         </div>
         <div class="modal-footer">
             <input type="submit" class="btn primary" value="Yes"/>
             <a href="#" class="btn secondary" data-dismiss="modal">No</a>
         </div>
-	</form>
+    </form>
 </div>
 
 
@@ -172,12 +173,12 @@ ${layout.menubar(section='designs')}
                         continue;
                     }
                     container.append(
-                        $("<div/>").addClass("clearfix")
-                          .append($("<label/>").text(params[key]))
-                          .append(
-                              $("<div/>").addClass("input")
-                                .append($("<input/>").attr("name", key).attr("type", "text"))
-                          )
+                            $("<div/>").addClass("clearfix")
+                                    .append($("<label/>").text(params[key]))
+                                    .append(
+                                    $("<div/>").addClass("input")
+                                            .append($("<input/>").attr("name", key).attr("type", "text"))
+                            )
                     )
                 }
                 // Good. We can submit now.
@@ -189,31 +190,29 @@ ${layout.menubar(section='designs')}
         });
 
         var oTable = $('#designTable').dataTable( {
-          "sPaginationType": "bootstrap",
-          "bLengthChange": false,
-          "sDom": "<'row'r>t<'row'<'span8'i><''p>>",
-		  "aoColumns": [
-				null,
-				null,
-				null,
-				null,
-				{ "sType": "date" },
-				{ "bSortable": false }
-			],
-			"aaSorting": [[ 4, "desc" ]]
+            "sPaginationType": "bootstrap",
+            "bLengthChange": false,
+            "sDom": "<'row'r>t<'row'<'span8'i><''p>>",
+            "aoColumns": [
+                null,
+                null,
+                null,
+                null,
+                { "sType": "alt-numeric" },
+                { "bSortable": false }
+            ],
+            "aaSorting": [[ 4, "desc" ]]
         });
 
         $("#filterInput").keyup(function() {
             oTable.fnFilter($(this).val());
         });
 
-		$("#installSamplesLink").click(function(){
+        $("#installSamplesLink").click(function(){
             $("#installSamples").modal("show");
         });
 
-		$("a[data-row-selector='true']").jHueRowSelector();
-
-
+        $("a[data-row-selector='true']").jHueRowSelector();
     });
 </script>
 ${commonfooter()}

+ 31 - 30
apps/jobsub/src/jobsub/templates/list_history.mako

@@ -15,12 +15,13 @@
 ## limitations under the License.
 
 <%!
-  import urllib
-  from django.template.defaultfilters import date, time
-  from desktop.views import commonheader, commonfooter
-  from django.utils.translation import ugettext, ungettext, get_language, activate
+    import urllib
+    import time as py_time
+    from django.template.defaultfilters import date, time
+    from desktop.views import commonheader, commonfooter
+    from django.utils.translation import ugettext, ungettext, get_language, activate
 
-  _ = ugettext
+    _ = ugettext
 %>
 <%namespace name="layout" file="layout.mako" />
 
@@ -33,24 +34,24 @@ ${layout.menubar(section='history')}
     <h1>Job Submission History</h1>
     <div class="well hueWell">
         <form class="form-search">
-			Filter: <input id="filterInput" class="input-xlarge search-query" placeholder="Search for username, name, etc...">
-		</form>
+            Filter: <input id="filterInput" class="input-xlarge search-query" placeholder="Search for username, name, etc...">
+        </form>
     </div>
 
 
     <table class="table table-condensed datatables" id="jobTable">
         <thead>
-            <tr>
-                <th>Oozie Job ID</th>
-                <th>Owner</th>
-                <th>Name</th>
-                <th>Type</th>
-                <th>Description</th>
-                <th>Submission Date</th>
-            </tr>
+        <tr>
+            <th>Oozie Job ID</th>
+            <th>Owner</th>
+            <th>Name</th>
+            <th>Type</th>
+            <th>Description</th>
+            <th>Submission Date</th>
+        </tr>
         </thead>
         <tbody>
-            %for record in history:
+                %for record in history:
                 <% design = record.design %>
                 <tr>
                     <td><a href="${url('jobsub.views.oozie_job', jobid=record.job_id)}">${record.job_id}</a></td>
@@ -58,9 +59,9 @@ ${layout.menubar(section='history')}
                     <td>${design.name}</td>
                     <td>${design.root_action.action_type}</td>
                     <td>${design.description}</td>
-                    <td>${date(record.submission_date)} ${time(record.submission_date).replace("p.m.","PM").replace("a.m.","AM")}</td>
+                    <td><span alt="${py_time.mktime(design.last_modified.timetuple())}">${date(record.submission_date)} ${time(record.submission_date).replace("p.m.","PM").replace("a.m.","AM")}</span></td>
                 </tr>
-            %endfor
+                %endfor
         </tbody>
     </table>
 </div>
@@ -68,18 +69,18 @@ ${layout.menubar(section='history')}
 <script type="text/javascript" charset="utf-8">
     $(document).ready(function() {
         var oTable = $('#jobTable').dataTable( {
-          'sPaginationType': 'bootstrap',
-          "bLengthChange": false,
-          "sDom": "<'row'r>t<'row'<'span8'i><''p>>",
-		  "aoColumns": [
-				null,
-				null,
-				null,
-				null,
-				null,
-				{ "sType": "date" }
-			],
-			"aaSorting": [[ 5, "desc" ]]
+            'sPaginationType': 'bootstrap',
+            "bLengthChange": false,
+            "sDom": "<'row'r>t<'row'<'span8'i><''p>>",
+            "aoColumns": [
+                null,
+                null,
+                null,
+                null,
+                null,
+                { "sType": "alt-numeric" }
+            ],
+            "aaSorting": [[ 5, "desc" ]]
         });
 
         $("#filterInput").keyup(function() {

+ 1 - 1
apps/jobsub/src/jobsub/templates/status_bar.mako

@@ -15,5 +15,5 @@
 ## limitations under the License.
 <%! import jobsub %>
 % if pending_count:
-<a href="${url(jobsub.views.watch)}" target="JobSub" class="hue-dock-status-pending">${pending_count} pending</a>
+<a href="${url(jobsub.views.watch)}">${pending_count} pending</a>
 % endif

+ 7 - 6
desktop/core/src/desktop/templates/common_header.mako

@@ -49,17 +49,18 @@ from desktop.lib.i18n import smart_unicode
   <script src="/static/js/Source/jHue/jquery.rowselector.js"></script>
   <script src="/static/ext/js/jquery/plugins/jquery.simpleplaceholder.js"></script>
   <script src="/static/ext/js/jquery/plugins/jquery.dataTables.1.8.2.min.js"></script>
+  <script src="/static/js/Source/jHue/jquery.datatables.sorting.js"></script>
   <script src="/static/ext/js/bootstrap.min.js"></script>
   <script src="/static/ext/js/fileuploader.js"></script>
 
   <script type="text/javascript" charset="utf-8">
     $(document).ready(function(){
       $("#username").jHueUsername({
-		onLoad: function(user){
-			$(".userProfile").attr("href","/useradmin/users/edit/"+user.username);
-			$("#usernameDropdown").show();
-		}
-	  });
+        onLoad: function(user){
+            $(".userProfile").attr("href","/useradmin/users/edit/"+user.username);
+            $("#usernameDropdown").show();
+        }
+      });
       $("input:text[placeholder]").simplePlaceholder();
       $(".submitter").keydown(function(e){
         if (e.keyCode==13){
@@ -87,7 +88,7 @@ from desktop.lib.i18n import smart_unicode
     <div class="navbar-inner">
       <div class="container-fluid">
         <a class="brand nav-tooltip" title="About Hue" href="/about">Hue</a>
-		<div id="usernameDropdown" class="btn-group pull-right hide">
+        <div id="usernameDropdown" class="btn-group pull-right hide">
           <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
             <i class="icon-user"></i> <span id="username"></span>
             <span class="caret"></span>

+ 30 - 0
desktop/core/static/js/Source/jHue/jquery.datatables.sorting.js

@@ -0,0 +1,30 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+jQuery.extend(jQuery.fn.dataTableExt.oSort, {
+    "alt-numeric-pre": function (a) {
+        var x = a.match(/title="*(-?[0-9\.]+)/)[1];
+        return parseFloat(x);
+    },
+
+    "alt-numeric-asc":function (a, b) {
+        return ((a < b) ? -1 : ((a > b) ? 1 : 0));
+    },
+
+    "alt-numeric-desc":function (a, b) {
+        return ((a < b) ? 1 : ((a > b) ? -1 : 0));
+    }
+});