Index: branches/HiveStatistics/sources/HeuristicLab.Services.Hive.Statistics/3.3/App_Code/ChartHelper.cshtml
===================================================================
--- branches/HiveStatistics/sources/HeuristicLab.Services.Hive.Statistics/3.3/App_Code/ChartHelper.cshtml	(revision 11030)
+++ branches/HiveStatistics/sources/HeuristicLab.Services.Hive.Statistics/3.3/App_Code/ChartHelper.cshtml	(revision 11036)
@@ -84,12 +84,4 @@
         });
         
-        @(destinationTag)Plot.replot({ resetAxes: true });
-        
-        $('#@destinationTag').contextmenu(function() {
-            @(destinationTag)Plot.resetZoom();
-            @(destinationTag)Plot.replot({ resetAxes: true });
-            return false;
-        });
-        
         $(window).resize(function() {
             @(destinationTag)Plot.replot({ resetAxes: true });
@@ -98,8 +90,8 @@
 }
 
-@helper RefreshChart(string destinationTag, string url, string startDateVar, string endDateVar)
-{
-  <text>
-  $.ajax({url: "@(new HtmlString(url))?start=" + @startDateVar + "&end=" + @endDateVar, datatype: "json", success: function(result) {
+@helper RefreshChart(string destinationTag, string url, string startDate, string endDate)
+{
+  <text>
+  $.ajax({url: "@(new HtmlString(url))?start=" + @startDate + "&end=" + @endDate, datatype: "json", success: function(result) {
     for (var i = 0; i < result.length; i++) {
       @(destinationTag)Plot.series[i].data = result[i];
@@ -110,5 +102,37 @@
 }
 
-@helper TasksForUser(string container, string destinationTag, string pageUrl, string taskUrl, string functionName, string userName, string limit, string startDate = null, string endDate = null, string jobId=null, string taskState=null, string pageNumber=null)
+@helper NumberPages(string records, string limit, string container, string functionName, string currentPage = null)
+{
+  <text>
+    if(@(records).length > @limit) {
+      $("#@container").append('<label class="pageTitle">Page: </label>')
+      var pages = Math.floor(@(records).length/@(limit)) + 1;
+      for(var i=0; i < pages; i++) {
+        $("#" + "@container").append('<a id="@(container)Page' + (i + 1) + '" class="page">' + (i + 1) + '</a>')
+      }
+      if(@currentPage != null) {
+        $("#@(container)Page" + @currentPage).css('color','#F7921D');
+      }
+      else {
+        $("#@(container)Page1").css('color','#F7921D');
+      }
+      $(".page").click(function () {
+        pageNumber = $(this).html();
+        @(functionName)();
+      });
+      if(@currentPage != null) {
+        @(records).splice(0,(@(currentPage)-1)*@(limit));
+        if (@(records).length > @(limit)) {
+          @(records).splice(@(limit), @(records).length - @(limit));
+        }
+      }
+      else {
+        @(records).splice(@(limit), @(records).length - @(limit));
+      }
+    }
+  </text>
+}
+
+@helper TasksForUser(string container, string destinationTag, string url, string functionName, string userName, string limit, string startDate = null, string endDate = null, string jobId=null, string taskState=null, string pageNumber=null)
 {
   <text>
@@ -134,25 +158,7 @@
       @:}
     }
-    $.ajax({async: false, url: "@(new HtmlString(pageUrl))" + GetRequest, datatype: "json", success: function(result) {
-      if(result > 1) {
-        $("#@container").append('<label class="pageTitle">Page: </label>')
-        for(var i=0; i < result; i++) {
-          $("#" + "@container").append('<a id="Page' + (i + 1) + '" class="page">' + (i + 1) + '</a>')
-        }
-        if(@pageNumber != null) {
-          $("#Page" + @pageNumber).css('color','#F7921D');
-        }
-        else {
-          $("#Page1").css('color','#F7921D');
-        }
-      }
-    }});
-    @if(pageNumber!=null) {
-      @:if(@(pageNumber)!=null) {
-        @:GetRequest += "&currentPage=" + @pageNumber;
-      @:}
-    }
-    $.ajax({url: "@(new HtmlString(taskUrl))" + GetRequest, datatype: "json", success: function(result) {
-      if(result.Key.length==0) {
+    $.ajax({
+      async: false, url: "@(new HtmlString(url))" + GetRequest, datatype: "json", success: function(result) {
+      if(result.length==0) {
         $("#@container").append(
           '<section class="chartContainer"><h1 class="title">' + @userName + ' has no tasks for the specified filters!</h1></section>'
@@ -164,6 +170,10 @@
       var seriesDescriptions = ["Time waiting","Time transferring","Time calculating"];
 
+      //Checks if multipage display, if it is then trims results to
+      //the results for the page to be displayed
+      @ChartHelper.NumberPages("result",limit,container,functionName,pageNumber)
+
       //Globally accesible, for use when resizing, eliminates extra DB request
-      window["numberTasks"] = result.Key.length;
+      window["numberTasks"] = result.length;
 
       //Set display of all errors to none, errors matching the tasks will be reset below
@@ -171,14 +181,16 @@
 
       //For each result create a seperate collapsable section with a chart and info label
-      for(var i = 0; i < result.Key.length; i++){
+      for(var i = 0; i < result.length; i++){
         $("#" + "@container").append(
-          '<section class="chartContainer"><h1 class="title" id="' + result.Key[i] + '">Task ' + result.Key[i] + 
+          '<section class="chartContainer"><h1 class="title" id="' + result[i].Key + '">Task ' + result[i].Key + 
           '</h1><button class="collapse" onclick="collapseSection(this)">+</button><div id="@(destinationTag)' + i + 
           '"></div><label id="@(destinationTag)' + i + 'Info"></label></section>'
         )
+        //Re-enables the error display if any of the tasks on the page have Ids matching
+        //those of errors
         $(".errorTask").each(function() { 
-          if($(this).html()==result.Key[i]) {
-            $("#" + result.Key[i]).css("color","red");
-            $("#" + result.Key[i]).append(" - ERROR");
+          if($(this).html()==result[i].Key) {
+            $("#" + result[i].Key).css("color","red");
+            $("#" + result[i].Key).append(" - ERROR");
             $(".errorContainer, .errorTitle, .underline").css('display','inline-block');
             $(this).css('display','inline-block');
@@ -186,7 +198,7 @@
           }
         });
-        waitTime = [result.Value[0][i]];
-        transferTime = [result.Value[1][i]];
-        runTime = [result.Value[2][i]];
+        waitTime = [result[i].Value[0]];
+        transferTime = [result[i].Value[1]];
+        runTime = [result[i].Value[2]];
         window["@(destinationTag)Plot" + i] = $.jqplot("@destinationTag" + i, [waitTime,transferTime,runTime], {
           seriesDefaults:{
@@ -225,20 +237,7 @@
         });
 
-        window["@(destinationTag)Plot" + i].replot({ resetAxes: true });
-
-        $('#@destinationTag' + i).contextmenu(function() {
-          window["@(destinationTag)Plot" + i].resetZoom();
-          window["@(destinationTag)Plot" + i].replot({ resetAxes: true });
-          return false;
-        });
-
         collapsedByDefault(document.getElementById("@(destinationTag)" + i));
       }
     }});
-
-    $(".page").click(function () {
-      pageNumber = $(this).html();
-      @(functionName)();
-    });
   </text>
 }
@@ -247,4 +246,5 @@
 {
   <text>
+  //Resize event, resets bar width for task charts and replots them
   $(window).resize(function() {
     for(var i = 0; i < numberTasks; i++) {
@@ -393,2 +393,168 @@
   </text>
 }
+
+@helper SlaveInfoChart(string destinationTag, string url, string limit, string startDate, string endDate, string userName, string functionName, string pageNumber=null)
+{
+  <text>
+  var GetRequest = "?limit=" + @(limit);
+  @if(startDate!=null) {
+    @:if(@(startDate)!=null) {
+      @:GetRequest += "&start=" + @startDate;
+    @:}
+  }
+  @if(endDate!=null) {
+    @:if(@(endDate)!=null) {
+      @:GetRequest += "&end=" + @endDate;
+    @:}
+  }
+  @if (userName != null) {
+    @:if(@(userName)!=null) {
+      @:GetRequest += "&userName=" + @userName;
+    @:}
+  }
+  $.ajax({
+    async: false, url: "@(new HtmlString(url))" + GetRequest, datatype: "json", success: function(result) {
+    $("#@(destinationTag)").html("");
+    var time = new Date();
+
+    //Checks if multipage display, if it is then trims results to
+    //the results for the page to be displayed
+    @ChartHelper.NumberPages("result",limit,destinationTag,functionName,pageNumber)
+
+    for(i = 0; i < result.length; i++) {
+      var coreSeries = [];
+      coreSeries[0] = [];
+      coreSeries[1] = [];
+      var memorySeries = [];
+      memorySeries[0] = [];
+      memorySeries[1] = [];
+      var cpuSeries = [];
+      cpuSeries[0] = [];
+      $("#@(destinationTag)").append('<section class="chartContainer"><h1 class="title" id="' + result[i][0].SlaveID + '">Slave ' 
+        + result[i][0].SlaveID + '</h1><button class="collapse" onclick="collapseSection(this)">+</button>' + 
+        '<div id="TotalUsedCores' + result[i][0].SlaveID + '"></div><div id="TotalUsedMemory' + result[i][0].SlaveID + '"></div>' + 
+        '<div id="CPUUtilization' + result[i][0].SlaveID + '"></div></section>');
+        for(j = 0; j < result[i].length; j++) {
+          time.setTime(result[i][j].Time.replace(/\D/g,''));
+          coreSeries[0].push([time.toUTCString(),result[i][j].TotalCores]);
+          coreSeries[1].push([time.toUTCString(),result[i][j].UsedCores]);
+          memorySeries[0].push([time.toUTCString(),(result[i][j].TotalMemory / 1000)]);
+          memorySeries[1].push([time.toUTCString(),(result[i][j].UsedMemory / 1000)]);
+          cpuSeries[0].push([time.toUTCString(),result[i][j].CPUUtilization]);
+        }
+      if(result[i].length > 1) {
+        @ChartHelper.LineChartGivenSeries("TotalUsedCores","result[i][0].SlaveID","coreSeries","Total/Used Cores")
+        @ChartHelper.LineChartGivenSeries("TotalUsedMemory","result[i][0].SlaveID","memorySeries","Total/Used Memory")
+        @ChartHelper.LineChartGivenSeries("CPUUtilization","result[i][0].SlaveID","cpuSeries","CPU Utilization",0,100,"%.1f%%")
+      }
+      else {
+        @ChartHelper.BarChartGivenSeries("TotalUsedCores","result[i][0].SlaveID","coreSeries","Total/Used Cores")
+        @ChartHelper.BarChartGivenSeries("TotalUsedMemory","result[i][0].SlaveID","memorySeries","Total/Used Memory")
+        @ChartHelper.BarChartGivenSeries("CPUUtilization","result[i][0].SlaveID","cpuSeries","CPU Utilization",0,100,"%.1f%%")
+      }
+      collapsedByDefault(document.getElementById("UsedTotalCores" + result[i][0].SlaveID));
+      collapsedByDefault(document.getElementById("UsedTotalMemory" + result[i][0].SlaveID));
+      collapsedByDefault(document.getElementById("CPUUtilization" + result[i][0].SlaveID));
+    }
+  }});
+  </text>
+}
+
+@helper LineChartGivenSeries(string destinationTag, string chartId, string series, string title, double? minY = null, double? maxY = null, string axisYFormat = null)
+{
+  <text>
+  window["@(destinationTag)" + @(chartId) + "Plot"] = $.jqplot("@(destinationTag)" + @(chartId), @series, {
+    title: "@title",
+    axes: {
+      xaxis: {
+        renderer: $.jqplot.DateAxisRenderer,
+        tickOptions:{formatString:'%b %#d, %y'},
+        pad: 0
+      },
+      yaxis: {
+        @if (axisYFormat != null) {
+          <text>
+          tickOptions: {
+              formatString: "@axisYFormat"
+          },
+          </text>
+        }
+        @if (minY != null) {
+            @:min: @minY,
+        }
+        @if (maxY != null) {
+            @:max: @maxY,
+        }
+        pad: 0
+      }
+    },
+    gridPadding: { left: 50, right: 10 },
+    cursor: {
+      show: true,
+      showTooltip: false,
+      zoom: true,
+      clickReset: false,
+      dblClickReset: false,
+      constrainZoomTo: 'x'
+    }
+  });
+
+  $(window).resize(function() {
+    window["@(destinationTag)" + @(chartId) + "Plot"].replot({ resetAxes: true });
+  });
+  </text>
+}
+
+@helper BarChartGivenSeries(string destinationTag, string chartId, string series, string title, double? minY = null, double? maxY = null, string axisYFormat = null)
+{
+  <text>
+  window["@(destinationTag)" + @(chartId) + "Plot"] = $.jqplot("@(destinationTag)" + @(chartId), @series, {
+    title: "@title",
+    seriesDefaults:{
+      renderer:$.jqplot.BarRenderer,
+      shadowAngle: 135,
+      pointLabels: {show: true, formatString: '%.2f'}
+    },
+    axes: {
+      xaxis: {
+        renderer: $.jqplot.CategoryAxisRenderer,
+        tickOptions:{formatString:'%b %#d, %y'},
+        pad: 0
+      },
+      yaxis: {
+        @if (axisYFormat != null) {
+          <text>
+          tickOptions: {
+              formatString: "@axisYFormat"
+          },
+          </text>
+        }
+        @if (minY != null) {
+            @:min: @minY,
+        }
+        @if (maxY != null) {
+            @:max: @maxY,
+        }
+        pad: 0
+      }
+    },
+    gridPadding: { left: 50, right: 10 },
+    cursor: {
+      show: true,
+      showTooltip: false,
+      zoom: true,
+      clickReset: false,
+      dblClickReset: false,
+      constrainZoomTo: 'x'
+    }
+  });
+
+  $(window).resize(function() {
+    $.each(window["@(destinationTag)" + @(chartId) + "Plot"].series, function(index, series) { 
+      series.barWidth = undefined; 
+    });
+    window["@(destinationTag)" + @(chartId) + "Plot"].replot({ resetAxes: true });
+  });
+
+  </text>
+}
