extensions/net.sf.basedb.labenv/trunk/resources/reports/boxplot.js

Code
Comments
Other
Rev Date Author Line
2303 02 Apr 14 olle 1   /**
2303 02 Apr 14 olle 2    *  Draws box plot in a canvas context of percentile data
2303 02 Apr 14 olle 3    *  supplied as a JSON object.
2303 02 Apr 14 olle 4    */
2401 06 May 14 olle 5   function createGraphPlot(boxPlotJsonObject, canvas, draw_area_wdt, draw_area_hgt, draw_scale_factor, viewType, storageIntervalInSeconds)
2401 06 May 14 olle 6   {
2401 06 May 14 olle 7     var ctx = canvas.getContext('2d');
2401 06 May 14 olle 8     
2401 06 May 14 olle 9     draw_area_wdt *= draw_scale_factor;
2401 06 May 14 olle 10     draw_area_hgt *= draw_scale_factor;
2401 06 May 14 olle 11     
2401 06 May 14 olle 12     ctx.clearRect(0, 0, draw_area_wdt, draw_area_hgt);
2401 06 May 14 olle 13     // Reserve area for scale and titles
4526 19 Jun 17 nicklas 14     var headerTitleHgt = 60 * draw_scale_factor;
4525 15 Jun 17 nicklas 15     var drect_xoff = 70 * draw_scale_factor;
4526 19 Jun 17 nicklas 16     var drect_yoff = 70 * draw_scale_factor;
2401 06 May 14 olle 17     var header_text_top_off = 5 * draw_scale_factor;
4525 15 Jun 17 nicklas 18     var text_axis_off = 5 * draw_scale_factor;
2401 06 May 14 olle 19     // Calculate remaining plot area
2401 06 May 14 olle 20     var drect_w = draw_area_wdt - 2*drect_xoff;
4525 15 Jun 17 nicklas 21     var drect_h = draw_area_hgt - drect_yoff - headerTitleHgt;
2401 06 May 14 olle 22     
2401 06 May 14 olle 23     // Font sizes
2401 06 May 14 olle 24     var base_font_size = 11 * draw_scale_factor; // 11px scaled
2401 06 May 14 olle 25     var header1_font_size = base_font_size + 5 * draw_scale_factor; // 16px scaled
2401 06 May 14 olle 26     var header2_font_size = base_font_size + 4 * draw_scale_factor; // 15px scaled
2401 06 May 14 olle 27     var top_right_font_size = base_font_size + 1 * draw_scale_factor; // 12px scaled
2401 06 May 14 olle 28     var top_left_font_size = base_font_size; // 11px scaled
2401 06 May 14 olle 29     var y_title_font_size = header2_font_size; // 15px scaled
2401 06 May 14 olle 30     var y_scale_font_size = base_font_size; // 11px scaled
2401 06 May 14 olle 31     var x_scale_font_size = base_font_size; // 11px scaled
2401 06 May 14 olle 32     
2401 06 May 14 olle 33     // Draw rectangle enclosing plot area
2401 06 May 14 olle 34     ctx.strokeStyle = "#000000";
2401 06 May 14 olle 35     ctx.strokeRect(fixCoordinate(ctx, drect_xoff), fixCoordinate(ctx, drect_yoff), Math.floor(drect_w), Math.floor(drect_h));
2401 06 May 14 olle 36     // Add top header title 
2401 06 May 14 olle 37     var headerTitleTop = getJSONData(boxPlotJsonObject, 'headerTitleTop');
2401 06 May 14 olle 38     drawHeaderTitleTopCenter(headerTitleTop, header1_font_size, header_text_top_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 39     // Add top plot title 
2401 06 May 14 olle 40     var titleTop = getJSONData(boxPlotJsonObject, 'titleTop');
2401 06 May 14 olle 41     drawTitleTopCenter(titleTop, header2_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 42     // Add bottom plot title 
2401 06 May 14 olle 43     var titleBottom = getJSONData(boxPlotJsonObject, 'titleBottom');
2401 06 May 14 olle 44     drawTitleBottomCenter(titleBottom, header2_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 45     // Add plot top right sub-title
2401 06 May 14 olle 46     var subTitleRight = getJSONData(boxPlotJsonObject, 'subTitleRight');
2401 06 May 14 olle 47     drawSubTitleTopRight(subTitleRight, top_right_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 48     // Add plot top left sub-titles
2401 06 May 14 olle 49     var subTitleLeft01 = getJSONData(boxPlotJsonObject, 'subTitleLeft01');
2401 06 May 14 olle 50     var subTitleLeft02 = getJSONData(boxPlotJsonObject, 'subTitleLeft02');
2401 06 May 14 olle 51     var subTitleLeft03 = getJSONData(boxPlotJsonObject, 'subTitleLeft03');
2401 06 May 14 olle 52     drawSubTitleTopLeft(subTitleLeft01, 0, top_left_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 53     drawSubTitleTopLeft(subTitleLeft02, 1, top_left_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 54     drawSubTitleTopLeft(subTitleLeft03, 2, top_left_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 55     // Add y-axis left title
2401 06 May 14 olle 56     var yAxisTitleLeft = getJSONData(boxPlotJsonObject, 'yAxisTitleLeft');
4525 15 Jun 17 nicklas 57     drawTitleYLeft(yAxisTitleLeft, y_title_font_size, ctx, drect_xoff-45*draw_scale_factor, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 58     // Add y-axis right title
2401 06 May 14 olle 59     var yAxisTitleRight = getJSONData(boxPlotJsonObject, 'yAxisTitleRight');
4525 15 Jun 17 nicklas 60     drawTitleYRight(yAxisTitleRight, y_title_font_size, ctx, drect_xoff+45*draw_scale_factor, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 61     // Calculate y-axis data value limits
2401 06 May 14 olle 62     var minDataValue = calculateMinDataValueSimple(boxPlotJsonObject);
2401 06 May 14 olle 63     var maxDataValue = calculateMaxDataValueSimple(boxPlotJsonObject);
2401 06 May 14 olle 64     if (viewType == 'overviewDisplay')
2401 06 May 14 olle 65     {
2401 06 May 14 olle 66       // Calculate min and max values for variables 'v1' and 'v3', respectively
2401 06 May 14 olle 67       minDataValue = calculateMinDataValueSimple2(boxPlotJsonObject);
2401 06 May 14 olle 68       maxDataValue = calculateMaxDataValueSimple2(boxPlotJsonObject);
2401 06 May 14 olle 69     }
2401 06 May 14 olle 70     // Calculate scale marker array
2401 06 May 14 olle 71     var numTicks = 7;
2401 06 May 14 olle 72     var scaleMarkerArr = calculateScaleMarkerArray(minDataValue, maxDataValue, numTicks);
2401 06 May 14 olle 73     // Add 2% buffer below min value and 5% buffer above max value
2401 06 May 14 olle 74     var dataValueRange = maxDataValue - minDataValue;
2401 06 May 14 olle 75     var min_y = minDataValue - 0.02*dataValueRange;
2401 06 May 14 olle 76     var max_y = maxDataValue + 0.05*dataValueRange;
2401 06 May 14 olle 77     // Trim scale marker array to only include values inside range
2401 06 May 14 olle 78     var trimmedScaleMarkerArr = trimScaleMarkerArray(scaleMarkerArr, min_y, max_y);
2401 06 May 14 olle 79     // Add small spacing buffer in pixels below and above value area
2401 06 May 14 olle 80     var min_yoff = 5 * draw_scale_factor;
2401 06 May 14 olle 81     var max_yoff = 5 * draw_scale_factor;
2401 06 May 14 olle 82     // Select number of decimals for scale marker text
2401 06 May 14 olle 83     // Select number of decimals to get one decimal digit different from 0, up to 3 decimals
2401 06 May 14 olle 84     var numberOfDecimals = 0;
2401 06 May 14 olle 85     for (i=0; i < trimmedScaleMarkerArr.length; i++)
2401 06 May 14 olle 86     {
2401 06 May 14 olle 87       var scaleValueTmp = trimmedScaleMarkerArr[i];
2401 06 May 14 olle 88       // Round scale value to 3 decimals (to make e.g. 2.99999993 -> 3.000)
2401 06 May 14 olle 89       scaleValueTmp = scaleValueTmp.toFixed(3);
2401 06 May 14 olle 90       var decimalPart = scaleValueTmp - Math.floor(scaleValueTmp);
2401 06 May 14 olle 91       if (decimalPart >= 0.05)
2401 06 May 14 olle 92       {
2401 06 May 14 olle 93         if (numberOfDecimals == 0 || numberOfDecimals > 1)
2401 06 May 14 olle 94         {
2401 06 May 14 olle 95           numberOfDecimals = 1;
2401 06 May 14 olle 96         }
2401 06 May 14 olle 97       }
2401 06 May 14 olle 98       else if (decimalPart >= 0.005)
2401 06 May 14 olle 99       {
2401 06 May 14 olle 100         if (numberOfDecimals == 0 || numberOfDecimals > 2)
2401 06 May 14 olle 101         {
2401 06 May 14 olle 102           numberOfDecimals = 2;
2401 06 May 14 olle 103         }
2401 06 May 14 olle 104       }
2401 06 May 14 olle 105       else if (decimalPart >= 0.0005)
2401 06 May 14 olle 106       {
2401 06 May 14 olle 107         if (numberOfDecimals == 0 || numberOfDecimals > 3)
2401 06 May 14 olle 108         {
2401 06 May 14 olle 109           numberOfDecimals = 3;
2401 06 May 14 olle 110         }
2401 06 May 14 olle 111       }
2401 06 May 14 olle 112     }
2401 06 May 14 olle 113     // Draw y-axis left scale markers with values
2401 06 May 14 olle 114     var scaleValue;
2401 06 May 14 olle 115     var markerText;
2401 06 May 14 olle 116     for (i=0; i < trimmedScaleMarkerArr.length; i++)
2401 06 May 14 olle 117     {
2401 06 May 14 olle 118       scaleValue = trimmedScaleMarkerArr[i];
2401 06 May 14 olle 119       markerText = scaleValue.toFixed(numberOfDecimals);
2401 06 May 14 olle 120       drawScaleMarkerYLeft(scaleValue, y_scale_font_size, markerText, min_y, max_y, min_yoff, max_yoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 121     }
2401 06 May 14 olle 122     // Find number of data points in graph plot
2401 06 May 14 olle 123     var dataPointsTot = 0;
2401 06 May 14 olle 124     for (var i in boxPlotJsonObject.percentileData)
2401 06 May 14 olle 125     {
2401 06 May 14 olle 126       dataPointsTot++;
2401 06 May 14 olle 127     }
2401 06 May 14 olle 128     // Set time delay to identify missing data points
4526 19 Jun 17 nicklas 129     var max_minutes_between_consequitive_data_points = 15;
2401 06 May 14 olle 130     if (storageIntervalInSeconds > 0)
2401 06 May 14 olle 131     {
2401 06 May 14 olle 132       var storageIntervalInMinutes = storageIntervalInSeconds/60;
4526 19 Jun 17 nicklas 133       max_minutes_between_consequitive_data_points = Math.max(5, 3*storageIntervalInMinutes);
2401 06 May 14 olle 134     }
2401 06 May 14 olle 135     // Draw data values as line graph
2401 06 May 14 olle 136     var min_x = 0;
2401 06 May 14 olle 137     var max_x = 24*60;
4526 19 Jun 17 nicklas 138     if (viewType == 'last24hours')
2401 06 May 14 olle 139     {
4526 19 Jun 17 nicklas 140       max_x = 25*60;
4526 19 Jun 17 nicklas 141     }
4526 19 Jun 17 nicklas 142     else if (viewType == 'weeklyDisplay')
4526 19 Jun 17 nicklas 143     {
2401 06 May 14 olle 144       max_x = 7*max_x;
2401 06 May 14 olle 145     }
2401 06 May 14 olle 146     else if (viewType == 'overviewDisplay')
2401 06 May 14 olle 147     {
2401 06 May 14 olle 148       // Set max_x to number of minutes between first midnight and midnight of last day with data
2401 06 May 14 olle 149       var num_data_points = boxPlotJsonObject.percentileData.length;
2401 06 May 14 olle 150       if (num_data_points > 0)
2401 06 May 14 olle 151       {
2401 06 May 14 olle 152         // Name of last data point is number of minutes since midnight of day for first point
2531 26 Jun 14 olle 153         var num_minutes_since_first_midnight = boxPlotJsonObject.latestPeriodName;
2401 06 May 14 olle 154         var num_full_days_since_first_midnight = Math.floor(num_minutes_since_first_midnight/(24*60));
2401 06 May 14 olle 155         var days_to_add = 1;
2401 06 May 14 olle 156         if (num_minutes_since_first_midnight % (24*60) == 0)
2401 06 May 14 olle 157         {
2401 06 May 14 olle 158           days_to_add = 0;
2401 06 May 14 olle 159         }
2401 06 May 14 olle 160         // Set max_x to number of minutes between first midnight and midnight of last day with data
2401 06 May 14 olle 161         max_x = (num_full_days_since_first_midnight + days_to_add)*24*60;
2401 06 May 14 olle 162       }
2401 06 May 14 olle 163       else
2401 06 May 14 olle 164       {
2401 06 May 14 olle 165         max_x = 7*max_x;
2401 06 May 14 olle 166       }
2401 06 May 14 olle 167       max_minutes_between_consequitive_data_points = 1.5*60*24;
2401 06 May 14 olle 168     }
2401 06 May 14 olle 169     // Draw graph
2401 06 May 14 olle 170     var x_old = 0;
2401 06 May 14 olle 171     var y_min_value_old = 0;
2401 06 May 14 olle 172     var y_mean_value_old = 0;
2401 06 May 14 olle 173     var y_max_value_old = 0;
2401 06 May 14 olle 174     for (var i in boxPlotJsonObject.percentileData)
2401 06 May 14 olle 175     {
2401 06 May 14 olle 176       var name = boxPlotJsonObject.percentileData[i].name;
2401 06 May 14 olle 177       var numItems = boxPlotJsonObject.percentileData[i].numItems;
2401 06 May 14 olle 178       var v1 = boxPlotJsonObject.percentileData[i].v1;
2401 06 May 14 olle 179       var v2 = boxPlotJsonObject.percentileData[i].v2;
2401 06 May 14 olle 180       var v3 = boxPlotJsonObject.percentileData[i].v3;
2401 06 May 14 olle 181       var v4 = boxPlotJsonObject.percentileData[i].v4;
2401 06 May 14 olle 182       var v5 = boxPlotJsonObject.percentileData[i].v5;
2401 06 May 14 olle 183       // Draw graph plot line segment
2401 06 May 14 olle 184       x1 = x_old;
2401 06 May 14 olle 185       y1_min_value = y_min_value_old;
2401 06 May 14 olle 186       y1_mean_value = y_mean_value_old;
2401 06 May 14 olle 187       y1_max_value = y_max_value_old;
2401 06 May 14 olle 188       x2 = name;
2401 06 May 14 olle 189       y2_min_value = v1;
2401 06 May 14 olle 190       y2_mean_value = v2;
2401 06 May 14 olle 191       y2_max_value = v3;
2401 06 May 14 olle 192       var x_pad = 10 * draw_scale_factor;
2401 06 May 14 olle 193       // Draw line segments starting after second data point
2401 06 May 14 olle 194       if (i > 0)
2401 06 May 14 olle 195       {
2401 06 May 14 olle 196         // Skip line segments containing missing data points
2401 06 May 14 olle 197         if ((x2 - x1) <= max_minutes_between_consequitive_data_points)
2401 06 May 14 olle 198         {
2401 06 May 14 olle 199           // Draw line segment
2401 06 May 14 olle 200           drawGraphPlotLineSegment(x1, y1_min_value, x2, y2_min_value, min_x, max_x, min_y, max_y, min_yoff, max_yoff, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h, draw_scale_factor, 2);
2401 06 May 14 olle 201           drawGraphPlotLineSegment(x1, y1_mean_value, x2, y2_mean_value, min_x, max_x, min_y, max_y, min_yoff, max_yoff, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h, draw_scale_factor, 3);
2401 06 May 14 olle 202           drawGraphPlotLineSegment(x1, y1_max_value, x2, y2_max_value, min_x, max_x, min_y, max_y, min_yoff, max_yoff, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h, draw_scale_factor, 2);
2401 06 May 14 olle 203           //drawGraphPlotLineSegmentY(x1, y1, x2, y2, x_min, x_max, y_min, y_max, min_off, max_off, x_pad, context, xoff, yoff, wdt, hgt, draw_scale_factor, linewidth);
2401 06 May 14 olle 204         }
2401 06 May 14 olle 205         else
2401 06 May 14 olle 206         {
2401 06 May 14 olle 207           // Draw single data point
2401 06 May 14 olle 208           drawGraphPlotDataPoint(x2, y2_min_value, min_x, max_x, min_y, max_y, min_yoff, max_yoff, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h, draw_scale_factor);
2401 06 May 14 olle 209           drawGraphPlotDataPoint(x2, y2_mean_value, min_x, max_x, min_y, max_y, min_yoff, max_yoff, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h, draw_scale_factor);
2401 06 May 14 olle 210           drawGraphPlotDataPoint(x2, y2_max_value, min_x, max_x, min_y, max_y, min_yoff, max_yoff, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h, draw_scale_factor);
2401 06 May 14 olle 211         }
2401 06 May 14 olle 212       }
2401 06 May 14 olle 213       x_old = x2;
2401 06 May 14 olle 214       y_min_value_old = y2_min_value;
2401 06 May 14 olle 215       y_mean_value_old = y2_mean_value;
2401 06 May 14 olle 216       y_max_value_old = y2_max_value;
2401 06 May 14 olle 217     }
2401 06 May 14 olle 218     // Draw graph for mean day values
2401 06 May 14 olle 219     x_old = 0;
2401 06 May 14 olle 220     y_old = 0;
2401 06 May 14 olle 221     for (var i in boxPlotJsonObject.percentileData)
2401 06 May 14 olle 222     {
2401 06 May 14 olle 223       var name = boxPlotJsonObject.percentileData[i].name;
2401 06 May 14 olle 224       var numItems = boxPlotJsonObject.percentileData[i].numItems;
2401 06 May 14 olle 225       var v1 = boxPlotJsonObject.percentileData[i].v1;
2401 06 May 14 olle 226       var v2 = boxPlotJsonObject.percentileData[i].v2;
2401 06 May 14 olle 227       var v3 = boxPlotJsonObject.percentileData[i].v3;
2401 06 May 14 olle 228       var v4 = boxPlotJsonObject.percentileData[i].v4;
2401 06 May 14 olle 229       var v5 = boxPlotJsonObject.percentileData[i].v5;
2401 06 May 14 olle 230       // Draw graph plot line segment
2401 06 May 14 olle 231       x1 = x_old;
2401 06 May 14 olle 232       y1 = y_old;
2401 06 May 14 olle 233       x2 = name;
2401 06 May 14 olle 234       y2 = v2;
2401 06 May 14 olle 235       var x_pad = 10 * draw_scale_factor;
2401 06 May 14 olle 236       // Draw line segments starting after second data point
2401 06 May 14 olle 237       if (i > 0)
2401 06 May 14 olle 238       {
2401 06 May 14 olle 239         // Skip line segments containing missing data points
2401 06 May 14 olle 240         if ((x2 - x1) <= max_minutes_between_consequitive_data_points)
2401 06 May 14 olle 241         {
2401 06 May 14 olle 242           // Draw line segment
2401 06 May 14 olle 243           drawGraphPlotLineSegment(x1, y1, x2, y2, min_x, max_x, min_y, max_y, min_yoff, max_yoff, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h, draw_scale_factor, 3);
2401 06 May 14 olle 244           //drawGraphPlotLineSegmentY(x1, y1, x2, y2, x_min, x_max, y_min, y_max, min_off, max_off, x_pad, context, xoff, yoff, wdt, hgt, draw_scale_factor, linewidth);
2401 06 May 14 olle 245         }
2401 06 May 14 olle 246         else
2401 06 May 14 olle 247         {
2401 06 May 14 olle 248           // Draw single data point
2401 06 May 14 olle 249           drawGraphPlotDataPoint(x2, y2, min_x, max_x, min_y, max_y, min_yoff, max_yoff, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h, draw_scale_factor);
2401 06 May 14 olle 250         }
2401 06 May 14 olle 251       }
2401 06 May 14 olle 252       x_old = x2;
2401 06 May 14 olle 253       y_old = y2;
2401 06 May 14 olle 254     }
2401 06 May 14 olle 255     // Draw x-axis bottom scale markers
4526 19 Jun 17 nicklas 256     if (viewType == 'last24hours')
2401 06 May 14 olle 257     {
4526 19 Jun 17 nicklas 258       var startHour = new Date().getHours();
4526 19 Jun 17 nicklas 259       for (var j = 0; j < 26; j++)
4526 19 Jun 17 nicklas 260       {
4526 19 Jun 17 nicklas 261         hourStr = (j+startHour) % 24;
4526 19 Jun 17 nicklas 262         if (hourStr < 10)
4526 19 Jun 17 nicklas 263         {
4526 19 Jun 17 nicklas 264           hourStr = '0' + hourStr;        
4526 19 Jun 17 nicklas 265         }
4526 19 Jun 17 nicklas 266         drawGraphPlotBoxScaleMarkerXBottom(j, 26, hourStr, x_scale_font_size, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
4526 19 Jun 17 nicklas 267         // Draw dotted vertical lines at x axis bottom scale marker values
4526 19 Jun 17 nicklas 268         var min_xoff = x_pad;
4526 19 Jun 17 nicklas 269         var max_xoff = x_pad;
4526 19 Jun 17 nicklas 270         drawDottedLineX(j, 0, 25, min_xoff, max_xoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
4526 19 Jun 17 nicklas 271       }
4526 19 Jun 17 nicklas 272     }
4526 19 Jun 17 nicklas 273     else if (viewType == 'dailyDisplay')
4526 19 Jun 17 nicklas 274     {
2401 06 May 14 olle 275       for (var j = 0; j < 25; j++)
2401 06 May 14 olle 276       {
2401 06 May 14 olle 277         hourStr = j;
2401 06 May 14 olle 278         if (j < 10)
2401 06 May 14 olle 279         {
2401 06 May 14 olle 280           hourStr = '0' + hourStr;        
2401 06 May 14 olle 281         }
2401 06 May 14 olle 282         drawGraphPlotBoxScaleMarkerXBottom(j, 25, hourStr, x_scale_font_size, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 283         // Draw dotted vertical lines at x axis bottom scale marker values
2401 06 May 14 olle 284         var min_xoff = x_pad;
2401 06 May 14 olle 285         var max_xoff = x_pad;
2401 06 May 14 olle 286         drawDottedLineX(j, 0, 24, min_xoff, max_xoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 287       }
2401 06 May 14 olle 288     }
2401 06 May 14 olle 289     else if (viewType == 'weeklyDisplay')
2401 06 May 14 olle 290     {
2401 06 May 14 olle 291       var jsonDayInWeekString = boxPlotJsonObject.dayInWeekString;
2401 06 May 14 olle 292       for (var j = 0; j < 8; j++)
2401 06 May 14 olle 293       {
2401 06 May 14 olle 294         dayIndexStr = j;
2401 06 May 14 olle 295         if (j < 10)
2401 06 May 14 olle 296         {
2401 06 May 14 olle 297           dayIndexStr = '0' + dayIndexStr;        
2401 06 May 14 olle 298         }
2401 06 May 14 olle 299         var dayInWeekStr = jsonDayInWeekString[dayIndexStr];
2401 06 May 14 olle 300         drawGraphPlotBoxScaleMarkerXBottom(j, 8, dayInWeekStr, x_scale_font_size, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 301         // Draw dotted vertical lines at x axis bottom scale marker values
2401 06 May 14 olle 302         var min_xoff = x_pad;
2401 06 May 14 olle 303         var max_xoff = x_pad;
2401 06 May 14 olle 304         drawDottedLineX(j, 0, 7, min_xoff, max_xoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 305       }
2401 06 May 14 olle 306     }
2401 06 May 14 olle 307     else if (viewType == 'overviewDisplay')
2401 06 May 14 olle 308     {
2401 06 May 14 olle 309       var jsonDayInWeekString = boxPlotJsonObject.dayInWeekString;
2401 06 May 14 olle 310       var numMarkerStrings = jsonDayInWeekString.numMarkers;
2401 06 May 14 olle 311       for (var j = 0; j < numMarkerStrings; j++)
2401 06 May 14 olle 312       {
2401 06 May 14 olle 313         dayIndexStr = j;
2401 06 May 14 olle 314         if (j < 10)
2401 06 May 14 olle 315         {
2401 06 May 14 olle 316           dayIndexStr = '0' + dayIndexStr;
2401 06 May 14 olle 317         }
2401 06 May 14 olle 318         if (j < 100)
2401 06 May 14 olle 319         {
2401 06 May 14 olle 320           dayIndexStr = '0' + dayIndexStr;
2401 06 May 14 olle 321         }
2401 06 May 14 olle 322         if (j < 1000)
2401 06 May 14 olle 323         {
2401 06 May 14 olle 324           dayIndexStr = '0' + dayIndexStr;
2401 06 May 14 olle 325         }
2401 06 May 14 olle 326         var dayInWeekStr = jsonDayInWeekString[dayIndexStr];
2401 06 May 14 olle 327         if (dayInWeekStr != null)
2401 06 May 14 olle 328         {
2401 06 May 14 olle 329           drawGraphPlotBoxScaleMarkerXBottom(j, numMarkerStrings, dayInWeekStr, x_scale_font_size, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 330           // Draw dotted vertical lines at x axis bottom scale marker values
2401 06 May 14 olle 331           var min_xoff = x_pad;
2401 06 May 14 olle 332           var max_xoff = x_pad;
2401 06 May 14 olle 333           drawDottedLineX(j, 0, numMarkerStrings - 1, min_xoff, max_xoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 334         }
2401 06 May 14 olle 335       }
2401 06 May 14 olle 336     }
2401 06 May 14 olle 337     // Draw y-axis right scale markers with values and horizontal dotted guide lines
2401 06 May 14 olle 338     for (var i in boxPlotJsonObject.valueGuideLinesY)
2401 06 May 14 olle 339     {
2401 06 May 14 olle 340       if (boxPlotJsonObject.valueGuideLinesY[i] != null)
2401 06 May 14 olle 341       {
2401 06 May 14 olle 342         var value = boxPlotJsonObject.valueGuideLinesY[i].value;
2401 06 May 14 olle 343         var valueText = boxPlotJsonObject.valueGuideLinesY[i].text;
2401 06 May 14 olle 344         // Draw y-axis right scale markers with values
2401 06 May 14 olle 345         drawScaleMarkerYRight(value, valueText, y_scale_font_size, min_y, max_y, min_yoff, max_yoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 346         // Draw dotted horizontal lines at y axis right scale marker values
2401 06 May 14 olle 347         drawDottedLineY(value, min_y, max_y, min_yoff, max_yoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2401 06 May 14 olle 348       }
2401 06 May 14 olle 349     }
2401 06 May 14 olle 350   }
2401 06 May 14 olle 351
2401 06 May 14 olle 352   /**
2401 06 May 14 olle 353    *  Draws box plot in a canvas context of percentile data
2401 06 May 14 olle 354    *  supplied as a JSON object.
2401 06 May 14 olle 355    */
2303 02 Apr 14 olle 356   function createBoxPlot(boxPlotJsonObject, canvas, draw_area_wdt, draw_area_hgt, draw_scale_factor)
2303 02 Apr 14 olle 357   {
2303 02 Apr 14 olle 358     var ctx = canvas.getContext('2d');
2303 02 Apr 14 olle 359     
2303 02 Apr 14 olle 360     draw_area_wdt *= draw_scale_factor;
2303 02 Apr 14 olle 361     draw_area_hgt *= draw_scale_factor;
2303 02 Apr 14 olle 362     
2303 02 Apr 14 olle 363     ctx.clearRect(0, 0, draw_area_wdt, draw_area_hgt);
2303 02 Apr 14 olle 364     // Reserve area for scale and titles
2303 02 Apr 14 olle 365     var headerTitleHgt = 25 * draw_scale_factor;
2303 02 Apr 14 olle 366     var drect_xoff = 60 * draw_scale_factor;
2303 02 Apr 14 olle 367     var drect_yoff = 70 * draw_scale_factor + headerTitleHgt;
2303 02 Apr 14 olle 368     var header_text_top_off = 5 * draw_scale_factor;
2303 02 Apr 14 olle 369     var text_axis_off = 40 * draw_scale_factor;
2303 02 Apr 14 olle 370     // Calculate remaining plot area
2303 02 Apr 14 olle 371     var drect_w = draw_area_wdt - 2*drect_xoff;
2303 02 Apr 14 olle 372     var drect_h = draw_area_hgt - 2*drect_yoff;
2303 02 Apr 14 olle 373     
2303 02 Apr 14 olle 374     // Font sizes
2303 02 Apr 14 olle 375     var base_font_size = 11 * draw_scale_factor; // 11px scaled
2303 02 Apr 14 olle 376     var header1_font_size = base_font_size + 5 * draw_scale_factor; // 16px scaled
2303 02 Apr 14 olle 377     var header2_font_size = base_font_size + 4 * draw_scale_factor; // 15px scaled
2303 02 Apr 14 olle 378     var top_right_font_size = base_font_size + 1 * draw_scale_factor; // 12px scaled
2303 02 Apr 14 olle 379     var top_left_font_size = base_font_size; // 11px scaled
2303 02 Apr 14 olle 380     var y_title_font_size = header2_font_size; // 15px scaled
2303 02 Apr 14 olle 381     var y_scale_font_size = base_font_size; // 11px scaled
2303 02 Apr 14 olle 382     var x_scale_font_size = base_font_size; // 11px scaled
2303 02 Apr 14 olle 383     
2303 02 Apr 14 olle 384     // Draw rectangle enclosing plot area
2303 02 Apr 14 olle 385     ctx.strokeStyle = "#000000";
2303 02 Apr 14 olle 386     ctx.strokeRect(fixCoordinate(ctx, drect_xoff), fixCoordinate(ctx, drect_yoff), Math.floor(drect_w), Math.floor(drect_h));
2303 02 Apr 14 olle 387     // Add top header title 
2303 02 Apr 14 olle 388     var headerTitleTop = getJSONData(boxPlotJsonObject, 'headerTitleTop');
2303 02 Apr 14 olle 389     drawHeaderTitleTopCenter(headerTitleTop, header1_font_size, header_text_top_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 390     // Add top plot title 
2303 02 Apr 14 olle 391     var titleTop = getJSONData(boxPlotJsonObject, 'titleTop');
2303 02 Apr 14 olle 392     drawTitleTopCenter(titleTop, header2_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 393     // Add bottom plot title 
2303 02 Apr 14 olle 394     var titleBottom = getJSONData(boxPlotJsonObject, 'titleBottom');
2303 02 Apr 14 olle 395     drawTitleBottomCenter(titleBottom, header2_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 396     // Add plot top right sub-title
2303 02 Apr 14 olle 397     var subTitleRight = getJSONData(boxPlotJsonObject, 'subTitleRight');
2303 02 Apr 14 olle 398     drawSubTitleTopRight(subTitleRight, top_right_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 399     // Add plot top left sub-titles
2303 02 Apr 14 olle 400     var subTitleLeft01 = getJSONData(boxPlotJsonObject, 'subTitleLeft01');
2303 02 Apr 14 olle 401     var subTitleLeft02 = getJSONData(boxPlotJsonObject, 'subTitleLeft02');
2303 02 Apr 14 olle 402     var subTitleLeft03 = getJSONData(boxPlotJsonObject, 'subTitleLeft03');
2303 02 Apr 14 olle 403     drawSubTitleTopLeft(subTitleLeft01, 0, top_left_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 404     drawSubTitleTopLeft(subTitleLeft02, 1, top_left_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 405     drawSubTitleTopLeft(subTitleLeft03, 2, top_left_font_size, text_axis_off, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 406     // Add y-axis left title
2303 02 Apr 14 olle 407     var yAxisTitleLeft = getJSONData(boxPlotJsonObject, 'yAxisTitleLeft');
2303 02 Apr 14 olle 408     drawTitleYLeft(yAxisTitleLeft, y_title_font_size, ctx, drect_xoff-35*draw_scale_factor, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 409     // Add y-axis right title
2303 02 Apr 14 olle 410     var yAxisTitleRight = getJSONData(boxPlotJsonObject, 'yAxisTitleRight');
2303 02 Apr 14 olle 411     drawTitleYRight(yAxisTitleRight, y_title_font_size, ctx, drect_xoff+35*draw_scale_factor, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 412     // Calculate y-axis data value limits
2303 02 Apr 14 olle 413     var minDataValue = calculateMinDataValue(boxPlotJsonObject);
2303 02 Apr 14 olle 414     var maxDataValue = calculateMaxDataValue(boxPlotJsonObject);
2303 02 Apr 14 olle 415     // Calculate scale marker array
2303 02 Apr 14 olle 416     var numTicks = 7;
2303 02 Apr 14 olle 417     var scaleMarkerArr = calculateScaleMarkerArray(minDataValue, maxDataValue, numTicks);
2303 02 Apr 14 olle 418     // Add 2% buffer below min value and 5% buffer above max value
2303 02 Apr 14 olle 419     var dataValueRange = maxDataValue - minDataValue;
2303 02 Apr 14 olle 420     var min_y = minDataValue - 0.02*dataValueRange;
2303 02 Apr 14 olle 421     var max_y = maxDataValue + 0.05*dataValueRange;
2303 02 Apr 14 olle 422     // Trim scale marker array to only include values inside range
2303 02 Apr 14 olle 423     var trimmedScaleMarkerArr = trimScaleMarkerArray(scaleMarkerArr, min_y, max_y);
2303 02 Apr 14 olle 424     // Add small spacing buffer in pixels below and above value area
2303 02 Apr 14 olle 425     var min_yoff = 5 * draw_scale_factor;
2303 02 Apr 14 olle 426     var max_yoff = 5 * draw_scale_factor;
2303 02 Apr 14 olle 427     // Select number of decimals for scale marker text
2303 02 Apr 14 olle 428     // Select number of decimals to get one decimal digit different from 0, up to 3 decimals
2303 02 Apr 14 olle 429     var numberOfDecimals = 0;
2303 02 Apr 14 olle 430     for (i=0; i < trimmedScaleMarkerArr.length; i++)
2303 02 Apr 14 olle 431     {
2303 02 Apr 14 olle 432       var scaleValueTmp = trimmedScaleMarkerArr[i];
2303 02 Apr 14 olle 433       // Round scale value to 3 decimals (to make e.g. 2.99999993 -> 3.000)
2303 02 Apr 14 olle 434       scaleValueTmp = scaleValueTmp.toFixed(3);
2303 02 Apr 14 olle 435       var decimalPart = scaleValueTmp - Math.floor(scaleValueTmp);
2303 02 Apr 14 olle 436       if (decimalPart >= 0.05)
2303 02 Apr 14 olle 437       {
2303 02 Apr 14 olle 438         if (numberOfDecimals == 0 || numberOfDecimals > 1)
2303 02 Apr 14 olle 439         {
2303 02 Apr 14 olle 440           numberOfDecimals = 1;
2303 02 Apr 14 olle 441         }
2303 02 Apr 14 olle 442       }
2303 02 Apr 14 olle 443       else if (decimalPart >= 0.005)
2303 02 Apr 14 olle 444       {
2303 02 Apr 14 olle 445         if (numberOfDecimals == 0 || numberOfDecimals > 2)
2303 02 Apr 14 olle 446         {
2303 02 Apr 14 olle 447           numberOfDecimals = 2;
2303 02 Apr 14 olle 448         }
2303 02 Apr 14 olle 449       }
2303 02 Apr 14 olle 450       else if (decimalPart >= 0.0005)
2303 02 Apr 14 olle 451       {
2303 02 Apr 14 olle 452         if (numberOfDecimals == 0 || numberOfDecimals > 3)
2303 02 Apr 14 olle 453         {
2303 02 Apr 14 olle 454           numberOfDecimals = 3;
2303 02 Apr 14 olle 455         }
2303 02 Apr 14 olle 456       }
2303 02 Apr 14 olle 457     }
2303 02 Apr 14 olle 458     // Draw y-axis left scale markers with values
2303 02 Apr 14 olle 459     var scaleValue;
2303 02 Apr 14 olle 460     var markerText;
2303 02 Apr 14 olle 461     for (i=0; i < trimmedScaleMarkerArr.length; i++)
2303 02 Apr 14 olle 462     {
2303 02 Apr 14 olle 463       scaleValue = trimmedScaleMarkerArr[i];
2303 02 Apr 14 olle 464       markerText = scaleValue.toFixed(numberOfDecimals);
2303 02 Apr 14 olle 465       drawScaleMarkerYLeft(scaleValue, y_scale_font_size, markerText, min_y, max_y, min_yoff, max_yoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 466     }
2303 02 Apr 14 olle 467     // Find number of boxes in box plot
2303 02 Apr 14 olle 468     var boxNumTot = 0;
2303 02 Apr 14 olle 469     for (var i in boxPlotJsonObject.percentileData)
2303 02 Apr 14 olle 470     {
2303 02 Apr 14 olle 471       boxNumTot++;
2303 02 Apr 14 olle 472     }
2303 02 Apr 14 olle 473     // Draw box plot boxes
2303 02 Apr 14 olle 474     for (var i in boxPlotJsonObject.percentileData)
2303 02 Apr 14 olle 475     {
2303 02 Apr 14 olle 476       var boxNum = i;
2303 02 Apr 14 olle 477       var name = boxPlotJsonObject.percentileData[i].name;
2303 02 Apr 14 olle 478       var numItems = boxPlotJsonObject.percentileData[i].numItems;
2303 02 Apr 14 olle 479       var v1 = boxPlotJsonObject.percentileData[i].v1;
2303 02 Apr 14 olle 480       var v2 = boxPlotJsonObject.percentileData[i].v2;
2303 02 Apr 14 olle 481       var v3 = boxPlotJsonObject.percentileData[i].v3;
2303 02 Apr 14 olle 482       var v4 = boxPlotJsonObject.percentileData[i].v4;
2303 02 Apr 14 olle 483       var v5 = boxPlotJsonObject.percentileData[i].v5;
2303 02 Apr 14 olle 484       // Draw box plot box
2303 02 Apr 14 olle 485       var x_pad = 10 * draw_scale_factor;
2303 02 Apr 14 olle 486       drawBoxPlotBoxY(boxNum, boxNumTot, v1, v2, v3, v4, v5, min_y, max_y, min_yoff, max_yoff, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h, draw_scale_factor);
2303 02 Apr 14 olle 487       drawBoxPlotBoxScaleMarkerXBottom(boxNum, boxNumTot, name, x_scale_font_size, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 488       drawBoxPlotBoxScaleMarkerXTop(boxNum, boxNumTot, numItems, x_scale_font_size, x_pad, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 489     }
2303 02 Apr 14 olle 490     // Draw y-axis right scale markers with values and horizontal dotted guide lines
2303 02 Apr 14 olle 491     for (var i in boxPlotJsonObject.valueGuideLinesY)
2303 02 Apr 14 olle 492     {
2303 02 Apr 14 olle 493       if (boxPlotJsonObject.valueGuideLinesY[i] != null)
2303 02 Apr 14 olle 494       {
2303 02 Apr 14 olle 495         var value = boxPlotJsonObject.valueGuideLinesY[i].value;
2303 02 Apr 14 olle 496         var valueText = boxPlotJsonObject.valueGuideLinesY[i].text;
2303 02 Apr 14 olle 497         // Draw y-axis right scale markers with values
2303 02 Apr 14 olle 498         drawScaleMarkerYRight(value, valueText, y_scale_font_size, min_y, max_y, min_yoff, max_yoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 499         // Draw dotted horizontal lines at y axis right scale marker values
2303 02 Apr 14 olle 500         drawDottedLineY(value, min_y, max_y, min_yoff, max_yoff, ctx, drect_xoff, drect_yoff, drect_w, drect_h);
2303 02 Apr 14 olle 501       }
2303 02 Apr 14 olle 502     }
2303 02 Apr 14 olle 503   }
2303 02 Apr 14 olle 504    
2303 02 Apr 14 olle 505   /**
2303 02 Apr 14 olle 506    *  Calculate min data value for percentiles in box plot JSON object.
2303 02 Apr 14 olle 507    */
2303 02 Apr 14 olle 508   function calculateMinDataValue(boxPlotJsonObject)
2303 02 Apr 14 olle 509   {
2303 02 Apr 14 olle 510     // Find number of boxes in box plot
2303 02 Apr 14 olle 511     var boxNumTot = 0;
2303 02 Apr 14 olle 512     for (var i in boxPlotJsonObject.percentileData)
2303 02 Apr 14 olle 513     {
2303 02 Apr 14 olle 514       boxNumTot++;
2303 02 Apr 14 olle 515     }
2303 02 Apr 14 olle 516     // Calculate min percentile value
2303 02 Apr 14 olle 517     var minValue = 1000000000;
2303 02 Apr 14 olle 518     for (var i in boxPlotJsonObject.percentileData)
2303 02 Apr 14 olle 519     {
2303 02 Apr 14 olle 520       var boxNum = i;
2303 02 Apr 14 olle 521       var v1 = boxPlotJsonObject.percentileData[i].v1;
2303 02 Apr 14 olle 522       var v5 = boxPlotJsonObject.percentileData[i].v5;
2303 02 Apr 14 olle 523       if (v1 < minValue)
2303 02 Apr 14 olle 524       {
2303 02 Apr 14 olle 525         minValue = v1;
2303 02 Apr 14 olle 526       }
2303 02 Apr 14 olle 527       if (v5 < minValue)
2303 02 Apr 14 olle 528       {
2303 02 Apr 14 olle 529         minValue = v5;
2303 02 Apr 14 olle 530       }
2303 02 Apr 14 olle 531     }
2303 02 Apr 14 olle 532     return minValue;
2303 02 Apr 14 olle 533   }
2303 02 Apr 14 olle 534    
2303 02 Apr 14 olle 535   /**
2303 02 Apr 14 olle 536    *  Calculate max data value for percentiles in box plot JSON object.
2303 02 Apr 14 olle 537    */
2303 02 Apr 14 olle 538   function calculateMaxDataValue(boxPlotJsonObject)
2303 02 Apr 14 olle 539   {
2303 02 Apr 14 olle 540     // Find number of boxes in box plot
2303 02 Apr 14 olle 541     var boxNumTot = 0;
2303 02 Apr 14 olle 542     for (var i in boxPlotJsonObject.percentileData)
2303 02 Apr 14 olle 543     {
2303 02 Apr 14 olle 544       boxNumTot++;
2303 02 Apr 14 olle 545     }
2303 02 Apr 14 olle 546     // Calculate max percentile value
2303 02 Apr 14 olle 547     var maxValue = -1;
2303 02 Apr 14 olle 548     for (var i in boxPlotJsonObject.percentileData)
2303 02 Apr 14 olle 549     {
2303 02 Apr 14 olle 550       var boxNum = i;
2303 02 Apr 14 olle 551       var v1 = boxPlotJsonObject.percentileData[i].v1;
2303 02 Apr 14 olle 552       var v5 = boxPlotJsonObject.percentileData[i].v5;
2303 02 Apr 14 olle 553       if (v1 > maxValue)
2303 02 Apr 14 olle 554       {
2303 02 Apr 14 olle 555         maxValue = v1;
2303 02 Apr 14 olle 556       }
2303 02 Apr 14 olle 557       if (v5 > maxValue)
2303 02 Apr 14 olle 558       {
2303 02 Apr 14 olle 559         maxValue = v5;
2303 02 Apr 14 olle 560       }
2303 02 Apr 14 olle 561     }
2303 02 Apr 14 olle 562     return maxValue;
2303 02 Apr 14 olle 563   }
2303 02 Apr 14 olle 564
2303 02 Apr 14 olle 565   /**
2401 06 May 14 olle 566    *  Calculate min data value for graph plot JSON object.
2401 06 May 14 olle 567    */
2401 06 May 14 olle 568   function calculateMinDataValueSimple(boxPlotJsonObject)
2401 06 May 14 olle 569   {
2401 06 May 14 olle 570     // Find number of boxes in box plot
2401 06 May 14 olle 571     var boxNumTot = 0;
2401 06 May 14 olle 572     for (var i in boxPlotJsonObject.percentileData)
2401 06 May 14 olle 573     {
2401 06 May 14 olle 574       boxNumTot++;
2401 06 May 14 olle 575     }
2401 06 May 14 olle 576     // Calculate min percentile value
2401 06 May 14 olle 577     var minValue = 1000000000;
2401 06 May 14 olle 578     for (var i in boxPlotJsonObject.percentileData)
2401 06 May 14 olle 579     {
2401 06 May 14 olle 580       var boxNum = i;
2401 06 May 14 olle 581       var v3 = boxPlotJsonObject.percentileData[i].v3;
2401 06 May 14 olle 582       if (v3 < minValue)
2401 06 May 14 olle 583       {
2401 06 May 14 olle 584         minValue = v3;
2401 06 May 14 olle 585       }
2401 06 May 14 olle 586     }
2401 06 May 14 olle 587     return minValue;
2401 06 May 14 olle 588   }
2401 06 May 14 olle 589    
2401 06 May 14 olle 590   /**
2401 06 May 14 olle 591    *  Calculate max data value for graph plot JSON object.
2401 06 May 14 olle 592    */
2401 06 May 14 olle 593   function calculateMaxDataValueSimple(boxPlotJsonObject)
2401 06 May 14 olle 594   {
2401 06 May 14 olle 595     // Find number of boxes in box plot
2401 06 May 14 olle 596     var boxNumTot = 0;
2401 06 May 14 olle 597     for (var i in boxPlotJsonObject.percentileData)
2401 06 May 14 olle 598     {
2401 06 May 14 olle 599       boxNumTot++;
2401 06 May 14 olle 600     }
2401 06 May 14 olle 601     // Calculate max percentile value
2401 06 May 14 olle 602     var maxValue = -1;
2401 06 May 14 olle 603     for (var i in boxPlotJsonObject.percentileData)
2401 06 May 14 olle 604     {
2401 06 May 14 olle 605       var boxNum = i;
2401 06 May 14 olle 606       var v3 = boxPlotJsonObject.percentileData[i].v3;
2401 06 May 14 olle 607       if (v3 > maxValue)
2401 06 May 14 olle 608       {
2401 06 May 14 olle 609         maxValue = v3;
2401 06 May 14 olle 610       }
2401 06 May 14 olle 611     }
2401 06 May 14 olle 612     return maxValue;
2401 06 May 14 olle 613   }
2401 06 May 14 olle 614
2401 06 May 14 olle 615   /**
2401 06 May 14 olle 616    *  Calculate min data value for graph plot JSON object.
2401 06 May 14 olle 617    *  Minimum value for variable v1.
2401 06 May 14 olle 618    */
2401 06 May 14 olle 619   function calculateMinDataValueSimple2(boxPlotJsonObject)
2401 06 May 14 olle 620   {
2401 06 May 14 olle 621     // Find number of boxes in box plot
2401 06 May 14 olle 622     var boxNumTot = 0;
2401 06 May 14 olle 623     for (var i in boxPlotJsonObject.percentileData)
2401 06 May 14 olle 624     {
2401 06 May 14 olle 625       boxNumTot++;
2401 06 May 14 olle 626     }
2401 06 May 14 olle 627     // Calculate min percentile value
2401 06 May 14 olle 628     var minValue = 1000000000;
2401 06 May 14 olle 629     for (var i in boxPlotJsonObject.percentileData)
2401 06 May 14 olle 630     {
2401 06 May 14 olle 631       var boxNum = i;
2401 06 May 14 olle 632       var v1 = boxPlotJsonObject.percentileData[i].v1;
2401 06 May 14 olle 633       if (v1 < minValue)
2401 06 May 14 olle 634       {
2401 06 May 14 olle 635         minValue = v1;
2401 06 May 14 olle 636       }
2401 06 May 14 olle 637     }
2401 06 May 14 olle 638     return minValue;
2401 06 May 14 olle 639   }
2401 06 May 14 olle 640    
2401 06 May 14 olle 641   /**
2401 06 May 14 olle 642    *  Calculate max data value for graph plot JSON object.
2401 06 May 14 olle 643    *  Maximum value for variable v3.
2401 06 May 14 olle 644    */
2401 06 May 14 olle 645   function calculateMaxDataValueSimple2(boxPlotJsonObject)
2401 06 May 14 olle 646   {
2401 06 May 14 olle 647     // Find number of boxes in box plot
2401 06 May 14 olle 648     var maxValue = calculateMaxDataValueSimple(boxPlotJsonObject);
2401 06 May 14 olle 649     return maxValue;
2401 06 May 14 olle 650   }
2401 06 May 14 olle 651
2401 06 May 14 olle 652   /**
2303 02 Apr 14 olle 653    *  Draws centered top header title in a canvas context, using coordinates
2303 02 Apr 14 olle 654    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 655    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 656    */
2303 02 Apr 14 olle 657   function drawHeaderTitleTopCenter(text, font_size, text_top_off, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 658   {
2303 02 Apr 14 olle 659     // Add centered top header title
2303 02 Apr 14 olle 660     context.font = font_size + 'px sans-serif';
2303 02 Apr 14 olle 661     context.textBaseline = 'top';
2303 02 Apr 14 olle 662     context.textAlign="center";
2303 02 Apr 14 olle 663     context.fillText(text, xoff + wdt/2, text_top_off);
2303 02 Apr 14 olle 664   }
2303 02 Apr 14 olle 665    
2303 02 Apr 14 olle 666   /**
2303 02 Apr 14 olle 667    *  Draws centered top title in a canvas context, using coordinates
2303 02 Apr 14 olle 668    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 669    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 670    */
2303 02 Apr 14 olle 671   function drawTitleTopCenter(text, font_size, text_axis_off, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 672   {
2303 02 Apr 14 olle 673     // Add centered top title
2303 02 Apr 14 olle 674     var text_axis_yoff = -text_axis_off;
2303 02 Apr 14 olle 675     context.font = font_size + 'px sans-serif';
2303 02 Apr 14 olle 676     context.textBaseline = 'bottom';
2303 02 Apr 14 olle 677     context.textAlign="center";
2303 02 Apr 14 olle 678     context.fillText(text, xoff + wdt/2, yoff + text_axis_yoff);
2303 02 Apr 14 olle 679   }
2303 02 Apr 14 olle 680    
2303 02 Apr 14 olle 681   /**
2303 02 Apr 14 olle 682    *  Draws centered bottom title in a canvas context, using coordinates
2303 02 Apr 14 olle 683    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 684    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 685    */
2303 02 Apr 14 olle 686   function drawTitleBottomCenter(text, font_size, text_axis_off, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 687   {
2303 02 Apr 14 olle 688     // Add centered bottom title
2303 02 Apr 14 olle 689     var text_axis_yoff = text_axis_off;
2303 02 Apr 14 olle 690     context.font = font_size + 'px sans-serif';
2303 02 Apr 14 olle 691     context.textBaseline = 'top';
2303 02 Apr 14 olle 692     context.textAlign="center";
2303 02 Apr 14 olle 693     context.fillText(text, xoff + wdt/2, yoff + hgt + text_axis_yoff);
2303 02 Apr 14 olle 694   }
2303 02 Apr 14 olle 695    
2303 02 Apr 14 olle 696   /**
2303 02 Apr 14 olle 697    *  Draws right-aligned top sub-title in a canvas context, using coordinates
2303 02 Apr 14 olle 698    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 699    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 700    */
2303 02 Apr 14 olle 701   function drawSubTitleTopRight(text, font_size, text_axis_off, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 702   {
2303 02 Apr 14 olle 703     // Add right-aligned top title
2303 02 Apr 14 olle 704     var text_axis_yoff = -text_axis_off;
2303 02 Apr 14 olle 705     context.font = font_size + 'px sans-serif';
2303 02 Apr 14 olle 706     context.textBaseline = 'bottom';
2303 02 Apr 14 olle 707     context.textAlign="right";
2303 02 Apr 14 olle 708     context.fillText(text, xoff + wdt, yoff + text_axis_yoff);
2303 02 Apr 14 olle 709   }
2303 02 Apr 14 olle 710    
2303 02 Apr 14 olle 711   /**
2303 02 Apr 14 olle 712    *  Draws left-aligned top sub-title in a canvas context, using coordinates
2303 02 Apr 14 olle 713    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 714    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 715    */
2303 02 Apr 14 olle 716   function drawSubTitleTopLeft(text, sub_title_num, font_size, text_axis_off, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 717   {
2303 02 Apr 14 olle 718     // Add left-aligned top title
2303 02 Apr 14 olle 719     var text_axis_yoff = -text_axis_off;
2303 02 Apr 14 olle 720     //var text_yoff = 35 - 11*(2 - sub_title_num);
2303 02 Apr 14 olle 721     var text_yoff = yoff + text_axis_yoff - (font_size+1)*(2 - sub_title_num);
2303 02 Apr 14 olle 722     context.font = font_size + 'px sans-serif';
2303 02 Apr 14 olle 723     context.textBaseline = 'bottom';
2303 02 Apr 14 olle 724     context.textAlign="left";
2303 02 Apr 14 olle 725     context.fillText(text, xoff, text_yoff);
2303 02 Apr 14 olle 726   }
2303 02 Apr 14 olle 727    
2303 02 Apr 14 olle 728   /**
2401 06 May 14 olle 729    *  Draws line segment between two data points.
2401 06 May 14 olle 730    */
2401 06 May 14 olle 731   function drawGraphPlotLineSegment(x1, y1, x2, y2, x_min, x_max, y_min, y_max, min_off, max_off, x_pad, context, xoff, yoff, wdt, hgt, draw_scale_factor, linewidth)
2401 06 May 14 olle 732   {
2401 06 May 14 olle 733     // Calculate value offsets
2401 06 May 14 olle 734     var eff_hgt = hgt - min_off - max_off;
2401 06 May 14 olle 735     var eff_wdt = wdt - 2*x_pad;
2401 06 May 14 olle 736     var x_range = x_max - x_min;
2401 06 May 14 olle 737     var y_range = y_max - y_min;
2401 06 May 14 olle 738     var x1_xoff = x_pad + eff_wdt*((x1 - x_min)/x_range);
2401 06 May 14 olle 739     var x2_xoff = x_pad + eff_wdt*((x2 - x_min)/x_range);
2401 06 May 14 olle 740     var y1_yoff = min_off + eff_hgt*((y1 - y_min)/y_range);
2401 06 May 14 olle 741     var y2_yoff = min_off + eff_hgt*((y2 - y_min)/y_range);
2401 06 May 14 olle 742     // Draw graph line between (x1, y1) and (x2, y2) (thick)
2401 06 May 14 olle 743     context.lineWidth = linewidth;
2401 06 May 14 olle 744     context.beginPath();
2401 06 May 14 olle 745     drawLine(x1_xoff, y1_yoff, x2_xoff, y2_yoff, context, xoff, yoff, wdt, hgt);
2401 06 May 14 olle 746     context.stroke();
2401 06 May 14 olle 747   }
2401 06 May 14 olle 748    
2401 06 May 14 olle 749   /**
2401 06 May 14 olle 750    *  Draws a data point.
2401 06 May 14 olle 751    */
2401 06 May 14 olle 752   function drawGraphPlotDataPoint(x, y, x_min, x_max, y_min, y_max, min_off, max_off, x_pad, context, xoff, yoff, wdt, hgt, draw_scale_factor)
2401 06 May 14 olle 753   {
2401 06 May 14 olle 754     // Calculate value offsets
2401 06 May 14 olle 755     var eff_hgt = hgt - min_off - max_off;
2401 06 May 14 olle 756     var eff_wdt = wdt - 2*x_pad;
2401 06 May 14 olle 757     var x_range = x_max - x_min;
2401 06 May 14 olle 758     var y_range = y_max - y_min;
2401 06 May 14 olle 759     var x_xoff = x_pad + eff_wdt*((x - x_min)/x_range);
2401 06 May 14 olle 760     var y_yoff = min_off + eff_hgt*((y - y_min)/y_range);
2401 06 May 14 olle 761     // Draw graph line data point at (x1, y1) (thick)
2401 06 May 14 olle 762     context.lineWidth = 3;
2401 06 May 14 olle 763     context.beginPath();
2401 06 May 14 olle 764     drawPoint(x_xoff, y_yoff, context, xoff, yoff, wdt, hgt);
2401 06 May 14 olle 765     context.stroke();
2401 06 May 14 olle 766   }
2401 06 May 14 olle 767    
2401 06 May 14 olle 768   /**
2401 06 May 14 olle 769    *  Draws x-axis bottom scale marker with text in a canvas context
2401 06 May 14 olle 770    *  for a graph plot box, using coordinates where x increases to the right
2401 06 May 14 olle 771    *  and y increases upwards, and where the offsets are added automatically.
2401 06 May 14 olle 772    */
2401 06 May 14 olle 773   function drawGraphPlotBoxScaleMarkerXBottom(markerNum, markerNumTot, value, font_size, x_pad, context, xoff, yoff, wdt, hgt)
2401 06 May 14 olle 774   {
2401 06 May 14 olle 775     // Calculate box width
2401 06 May 14 olle 776     var width_per_marker = (wdt - 2*x_pad)/(markerNumTot - 1);
2401 06 May 14 olle 777     // Reserve 20% as space between boxes
2401 06 May 14 olle 778     //var box_pad = width_per_box/10;
2401 06 May 14 olle 779     //var box_wdt = width_per_box*4/5;
2401 06 May 14 olle 780     // Calculate marker center offset
2401 06 May 14 olle 781     //var marker_xmin = x_pad + markerNum*width_per_box + box_pad;
2401 06 May 14 olle 782     var marker_xcenter = x_pad + markerNum*width_per_marker;
2401 06 May 14 olle 783     //var box_xmax = box_xmin + box_wdt;
2401 06 May 14 olle 784     // Draw scale marker
2401 06 May 14 olle 785     var marker_len = font_size;
2401 06 May 14 olle 786     context.lineWidth = 1;
2401 06 May 14 olle 787     context.beginPath();
2401 06 May 14 olle 788     drawLine(marker_xcenter, 0, marker_xcenter, -marker_len, context, xoff, yoff, wdt, hgt);
2401 06 May 14 olle 789     context.stroke();
2401 06 May 14 olle 790     // Draw scale marker value
2401 06 May 14 olle 791     var value_text = '' + value;
2401 06 May 14 olle 792     var text_axis_yoff = 1.5*font_size;
2401 06 May 14 olle 793     var text_marker_xoff = font_size / 2;
2401 06 May 14 olle 794     context.save();
2401 06 May 14 olle 795     context.font = font_size + 'px sans-serif';
2401 06 May 14 olle 796     context.textBaseline = 'bottom';
2401 06 May 14 olle 797     context.translate(xoff + marker_xcenter + text_marker_xoff, yoff + hgt + text_axis_yoff);
2401 06 May 14 olle 798     context.rotate(-Math.PI/2);
2401 06 May 14 olle 799     context.textAlign = "right";
2401 06 May 14 olle 800     context.fillText(value_text, 0, 0);
2401 06 May 14 olle 801     context.restore();
2401 06 May 14 olle 802   }
2401 06 May 14 olle 803    
2401 06 May 14 olle 804   /**
2303 02 Apr 14 olle 805    *  Draws vertical box plot box with five percentiles.
2303 02 Apr 14 olle 806    *  Box numbers starts from 0 at the left.
2303 02 Apr 14 olle 807    */
2303 02 Apr 14 olle 808   function drawBoxPlotBoxY(boxNum, boxNumTot, v1, v2, v3, v4, v5, min, max, min_off, max_off, x_pad, context, xoff, yoff, wdt, hgt, draw_scale_factor)
2303 02 Apr 14 olle 809   {
2303 02 Apr 14 olle 810     // Calculate value offsets
2303 02 Apr 14 olle 811     var eff_hgt = hgt - min_off - max_off;
2303 02 Apr 14 olle 812     var value_range = max - min;
2303 02 Apr 14 olle 813     var v1_yoff = min_off + eff_hgt*((v1 - min)/value_range);
2303 02 Apr 14 olle 814     var v2_yoff = min_off + eff_hgt*((v2 - min)/value_range);
2303 02 Apr 14 olle 815     var v3_yoff = min_off + eff_hgt*((v3 - min)/value_range);
2303 02 Apr 14 olle 816     var v4_yoff = min_off + eff_hgt*((v4 - min)/value_range);
2303 02 Apr 14 olle 817     var v5_yoff = min_off + eff_hgt*((v5 - min)/value_range);
2303 02 Apr 14 olle 818     // Calculate box width
2303 02 Apr 14 olle 819     var width_per_box = (wdt - 2*x_pad)/boxNumTot;
2303 02 Apr 14 olle 820     // Reserve 20% as space between boxes
2303 02 Apr 14 olle 821     var box_pad = width_per_box/10;
2303 02 Apr 14 olle 822     var box_wdt = width_per_box*4/5;
2303 02 Apr 14 olle 823     // Calculate box min, center, and max offsets
2303 02 Apr 14 olle 824     var box_xmin = x_pad + boxNum*width_per_box + box_pad;
2303 02 Apr 14 olle 825     var box_xcenter = box_xmin + box_wdt/2;
2303 02 Apr 14 olle 826     var box_xmax = box_xmin + box_wdt;
2303 02 Apr 14 olle 827     // Draw box rectangle
2303 02 Apr 14 olle 828     drawRect(box_xmin, v2_yoff, box_wdt, v4_yoff - v2_yoff, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 829     // Draw median line (thick)
2303 02 Apr 14 olle 830     context.lineWidth = 3;
2303 02 Apr 14 olle 831     context.beginPath();
2303 02 Apr 14 olle 832     drawLine(box_xmin, v3_yoff, box_xmax, v3_yoff, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 833     context.stroke();
2303 02 Apr 14 olle 834     // Draw line between first percentile and box (dashed line)
2303 02 Apr 14 olle 835     context.lineWidth = 1;
2303 02 Apr 14 olle 836     context.beginPath();
2303 02 Apr 14 olle 837     //drawLine(box_xcenter, v1_yoff, box_xcenter, v2_yoff, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 838     var dash_size = 5 * draw_scale_factor;
2303 02 Apr 14 olle 839     drawDashedLineVertical(box_xcenter, v1_yoff, v2_yoff, context, xoff, yoff, wdt, hgt, dash_size, dash_size);
2303 02 Apr 14 olle 840     // Draw short horizontal line at end of line
2303 02 Apr 14 olle 841     drawLine(box_xcenter - box_wdt/4, v1_yoff, box_xcenter + box_wdt/4, v1_yoff, context, xoff, yoff, wdt, hgt);    
2303 02 Apr 14 olle 842     context.stroke();
2303 02 Apr 14 olle 843     // Draw line between box and fifth percentile (dashed line)
2303 02 Apr 14 olle 844     context.lineWidth = 1;
2303 02 Apr 14 olle 845     context.beginPath();
2303 02 Apr 14 olle 846     //drawLine(box_xcenter, v4_yoff, box_xcenter, v5_yoff, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 847     drawDashedLineVertical(box_xcenter, v4_yoff, v5_yoff, context, xoff, yoff, wdt, hgt, dash_size, dash_size);
2303 02 Apr 14 olle 848     // Draw short horizontal line at end of line
2303 02 Apr 14 olle 849     drawLine(box_xcenter - box_wdt/4, v5_yoff, box_xcenter + box_wdt/4, v5_yoff, context, xoff, yoff, wdt, hgt);    
2303 02 Apr 14 olle 850     context.stroke();
2303 02 Apr 14 olle 851   }
2303 02 Apr 14 olle 852    
2303 02 Apr 14 olle 853   /**
2303 02 Apr 14 olle 854    *  Draws x-axis bottom scale marker with text in a canvas context
2303 02 Apr 14 olle 855    *  for a box plot box, using coordinates where x increases to the right
2303 02 Apr 14 olle 856    *  and y increases upwards, and where the offsets are added automatically.
2303 02 Apr 14 olle 857    */
2303 02 Apr 14 olle 858   function drawBoxPlotBoxScaleMarkerXBottom(boxNum, boxNumTot, value, font_size, x_pad, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 859   {
2303 02 Apr 14 olle 860     // Calculate box width
2303 02 Apr 14 olle 861     var width_per_box = (wdt - 2*x_pad)/boxNumTot;
2303 02 Apr 14 olle 862     // Reserve 20% as space between boxes
2303 02 Apr 14 olle 863     var box_pad = width_per_box/10;
2303 02 Apr 14 olle 864     var box_wdt = width_per_box*4/5;
2303 02 Apr 14 olle 865     // Calculate box min, center, and max offsets
2303 02 Apr 14 olle 866     var box_xmin = x_pad + boxNum*width_per_box + box_pad;
2303 02 Apr 14 olle 867     var box_xcenter = box_xmin + box_wdt/2;
2303 02 Apr 14 olle 868     var box_xmax = box_xmin + box_wdt;
2303 02 Apr 14 olle 869     // Draw scale marker
2303 02 Apr 14 olle 870     var marker_len = font_size;
2303 02 Apr 14 olle 871     context.lineWidth = 1;
2303 02 Apr 14 olle 872     context.beginPath();
2303 02 Apr 14 olle 873     drawLine(box_xcenter, 0, box_xcenter, -marker_len, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 874     context.stroke();
2303 02 Apr 14 olle 875     // Draw scale marker value
2303 02 Apr 14 olle 876     var value_text = '' + value;
2303 02 Apr 14 olle 877     var text_axis_yoff = 1.5*font_size;
2303 02 Apr 14 olle 878     var text_marker_xoff = font_size / 2;
2303 02 Apr 14 olle 879     context.save();
2303 02 Apr 14 olle 880     context.font = font_size + 'px sans-serif';
2303 02 Apr 14 olle 881     context.textBaseline = 'bottom';
2303 02 Apr 14 olle 882     context.translate(xoff + box_xcenter + text_marker_xoff, yoff + hgt + text_axis_yoff);
2303 02 Apr 14 olle 883     context.rotate(-Math.PI/2);
2303 02 Apr 14 olle 884     context.textAlign = "right";
2303 02 Apr 14 olle 885     context.fillText(value_text, 0, 0);
2303 02 Apr 14 olle 886     context.restore();
2303 02 Apr 14 olle 887   }
2303 02 Apr 14 olle 888    
2303 02 Apr 14 olle 889   /**
2303 02 Apr 14 olle 890    *  Draws x-axis top scale marker with text in a canvas context
2303 02 Apr 14 olle 891    *  for a box plot box, using coordinates where x increases to the right
2303 02 Apr 14 olle 892    *  and y increases upwards, and where the offsets are added automatically.
2303 02 Apr 14 olle 893    */
2303 02 Apr 14 olle 894   function drawBoxPlotBoxScaleMarkerXTop(boxNum, boxNumTot, value, font_size, x_pad, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 895   {
2303 02 Apr 14 olle 896     // Calculate box width
2303 02 Apr 14 olle 897     var width_per_box = (wdt - 2*x_pad)/boxNumTot;
2303 02 Apr 14 olle 898     // Reserve 20% as space between boxes
2303 02 Apr 14 olle 899     var box_pad = width_per_box/10;
2303 02 Apr 14 olle 900     var box_wdt = width_per_box*4/5;
2303 02 Apr 14 olle 901     // Calculate box min, center, and max offsets
2303 02 Apr 14 olle 902     var box_xmin = x_pad + boxNum*width_per_box + box_pad;
2303 02 Apr 14 olle 903     var box_xcenter = box_xmin + box_wdt/2;
2303 02 Apr 14 olle 904     var box_xmax = box_xmin + box_wdt;
2303 02 Apr 14 olle 905     // Draw scale marker
2303 02 Apr 14 olle 906     var marker_len = font_size;
2303 02 Apr 14 olle 907     context.lineWidth = 1;
2303 02 Apr 14 olle 908     context.beginPath();
2303 02 Apr 14 olle 909     drawLine(box_xcenter, hgt, box_xcenter, hgt + marker_len, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 910     context.stroke();
2303 02 Apr 14 olle 911     // Draw scale marker value
2303 02 Apr 14 olle 912     var value_text = '' + value;
2303 02 Apr 14 olle 913     var text_axis_yoff = 1.5*font_size;
2303 02 Apr 14 olle 914     var text_marker_xoff = font_size / 2;
2303 02 Apr 14 olle 915     context.save();
2303 02 Apr 14 olle 916     context.font = font_size + 'px sans-serif';
2303 02 Apr 14 olle 917     context.textBaseline = 'bottom';
2303 02 Apr 14 olle 918     context.translate(xoff + box_xcenter + text_marker_xoff, yoff - text_axis_yoff);
2303 02 Apr 14 olle 919     context.rotate(-Math.PI/2);
2303 02 Apr 14 olle 920     context.textAlign = "left";
2303 02 Apr 14 olle 921     context.fillText(value_text, 0, 0);
2303 02 Apr 14 olle 922     context.restore();
2303 02 Apr 14 olle 923   }
2303 02 Apr 14 olle 924    
2303 02 Apr 14 olle 925   /**
2303 02 Apr 14 olle 926    *  Draws y-axis left scale marker with text in a canvas context, using coordinates
2303 02 Apr 14 olle 927    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 928    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 929    */
2303 02 Apr 14 olle 930   function drawScaleMarkerYLeft(value, font_size, markerText, min, max, min_off, max_off, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 931   {
2303 02 Apr 14 olle 932     var eff_hgt = hgt - min_off - max_off;
2303 02 Apr 14 olle 933     var value_range = max - min;
2303 02 Apr 14 olle 934     var marker_yoff = min_off + eff_hgt*((value - min)/value_range);
2303 02 Apr 14 olle 935     var marker_len = font_size;
2303 02 Apr 14 olle 936     // Draw scale marker
2303 02 Apr 14 olle 937     context.lineWidth = 1;
2303 02 Apr 14 olle 938     context.beginPath();
2303 02 Apr 14 olle 939     drawLine(0, marker_yoff, -marker_len, marker_yoff, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 940     context.stroke();
2303 02 Apr 14 olle 941     // Draw scale marker value
2303 02 Apr 14 olle 942     var value_text = '' + markerText;
2303 02 Apr 14 olle 943     var text_axis_xoff = -1.5*font_size;
2303 02 Apr 14 olle 944     var text_marker_yoff = -font_size / 2;
2303 02 Apr 14 olle 945     context.font = font_size+'px sans-serif';
2303 02 Apr 14 olle 946     context.textBaseline = 'bottom';
2303 02 Apr 14 olle 947     context.textAlign="right";
2303 02 Apr 14 olle 948     drawText(value_text, text_axis_xoff, marker_yoff + text_marker_yoff, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 949   }
2303 02 Apr 14 olle 950    
2303 02 Apr 14 olle 951   /**
2303 02 Apr 14 olle 952    *  Draws y-axis right scale marker with text in a canvas context, using coordinates
2303 02 Apr 14 olle 953    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 954    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 955    */
2303 02 Apr 14 olle 956   function drawScaleMarkerYRight(value, markerText, font_size, min, max, min_off, max_off, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 957   {
2303 02 Apr 14 olle 958     var eff_hgt = hgt - min_off - max_off;
2303 02 Apr 14 olle 959     var value_range = max - min;
2303 02 Apr 14 olle 960     var marker_yoff = min_off + eff_hgt*((value - min)/value_range);
2303 02 Apr 14 olle 961     var marker_len = font_size;
2303 02 Apr 14 olle 962     // Draw scale marker
2303 02 Apr 14 olle 963     context.lineWidth = 1;
2303 02 Apr 14 olle 964     context.beginPath();
2303 02 Apr 14 olle 965     drawLine(wdt, marker_yoff, wdt + marker_len, marker_yoff, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 966     context.stroke();
2303 02 Apr 14 olle 967     // Draw scale marker value
2303 02 Apr 14 olle 968     var value_text = '' + markerText;
2303 02 Apr 14 olle 969     var text_axis_xoff = font_size * 1.5;
2303 02 Apr 14 olle 970     var text_marker_yoff = -font_size / 2;
2303 02 Apr 14 olle 971     context.font = font_size + 'px sans-serif';
2303 02 Apr 14 olle 972     context.textBaseline = 'bottom';
2303 02 Apr 14 olle 973     context.textAlign="left";
2303 02 Apr 14 olle 974     drawText(value_text, wdt + text_axis_xoff, marker_yoff + text_marker_yoff, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 975   }
2303 02 Apr 14 olle 976    
2303 02 Apr 14 olle 977   /**
2401 06 May 14 olle 978    *  Draws dotted line for x-value in a canvas context, using coordinates
2401 06 May 14 olle 979    *  where x increases to the right and y increases upwards,
2401 06 May 14 olle 980    *  and where the offsets are added automatically.
2401 06 May 14 olle 981    */
2401 06 May 14 olle 982   function drawDottedLineX(value, min, max, min_off, max_off, context, xoff, yoff, wdt, hgt)
2401 06 May 14 olle 983   {
2401 06 May 14 olle 984     var eff_wdt = wdt - min_off - max_off;
2401 06 May 14 olle 985     var value_range = max - min;
2401 06 May 14 olle 986     var line_xoff = min_off + eff_wdt*((value - min)/value_range);
2401 06 May 14 olle 987     // Draw dotted line for x-value
2401 06 May 14 olle 988     context.lineWidth = 1;
2401 06 May 14 olle 989     context.beginPath();
2401 06 May 14 olle 990     drawDottedLineVertical(line_xoff, 0, hgt, context, xoff, yoff, wdt, hgt);
2401 06 May 14 olle 991     context.stroke();
2401 06 May 14 olle 992   }
2401 06 May 14 olle 993    
2401 06 May 14 olle 994   /**
2303 02 Apr 14 olle 995    *  Draws dotted line for y-value in a canvas context, using coordinates
2303 02 Apr 14 olle 996    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 997    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 998    */
2303 02 Apr 14 olle 999   function drawDottedLineY(value, min, max, min_off, max_off, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 1000   {
2303 02 Apr 14 olle 1001     var eff_hgt = hgt - min_off - max_off;
2303 02 Apr 14 olle 1002     var value_range = max - min;
2303 02 Apr 14 olle 1003     var line_yoff = min_off + eff_hgt*((value - min)/value_range);
2303 02 Apr 14 olle 1004     // Draw dotted line for y-value
2303 02 Apr 14 olle 1005     context.lineWidth = 1;
2303 02 Apr 14 olle 1006     context.beginPath();
2303 02 Apr 14 olle 1007     drawDottedLineHorizontal(0, wdt, line_yoff, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 1008     context.stroke();
2303 02 Apr 14 olle 1009   }
2303 02 Apr 14 olle 1010    
2303 02 Apr 14 olle 1011   /**
2303 02 Apr 14 olle 1012    *  Draws y-axis left scale title in a canvas context, using coordinates
2303 02 Apr 14 olle 1013    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 1014    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 1015    */
2303 02 Apr 14 olle 1016   function drawTitleYLeft(title, font_size, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 1017   {
2303 02 Apr 14 olle 1018     // Add y axis title
2303 02 Apr 14 olle 1019     context.save();
2303 02 Apr 14 olle 1020     context.font = font_size + 'px sans-serif';
2303 02 Apr 14 olle 1021     context.textBaseline = 'bottom';
2303 02 Apr 14 olle 1022     context.translate(xoff, yoff + hgt/2);
2303 02 Apr 14 olle 1023     context.rotate(-Math.PI/2);
2303 02 Apr 14 olle 1024     context.textAlign = "center";
2303 02 Apr 14 olle 1025     context.fillText(title, 0, 0);
2303 02 Apr 14 olle 1026     context.restore();
2303 02 Apr 14 olle 1027   }
2303 02 Apr 14 olle 1028    
2303 02 Apr 14 olle 1029   /**
2303 02 Apr 14 olle 1030    *  Draws y-axis right scale title in a canvas context, using coordinates
2303 02 Apr 14 olle 1031    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 1032    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 1033    */
2303 02 Apr 14 olle 1034   function drawTitleYRight(title, font_size, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 1035   {
2303 02 Apr 14 olle 1036     // Add y axis title
2303 02 Apr 14 olle 1037     context.save();
2303 02 Apr 14 olle 1038     context.font = font_size + 'px sans-serif';
2303 02 Apr 14 olle 1039     context.textBaseline = 'top';
2303 02 Apr 14 olle 1040     context.translate(xoff + wdt, yoff + hgt/2);
2303 02 Apr 14 olle 1041     context.rotate(-Math.PI/2);
2303 02 Apr 14 olle 1042     context.textAlign = "center";
2303 02 Apr 14 olle 1043     context.fillText(title, 0, 0);
2303 02 Apr 14 olle 1044     context.restore();
2303 02 Apr 14 olle 1045   }
2303 02 Apr 14 olle 1046    
2303 02 Apr 14 olle 1047   /**
2303 02 Apr 14 olle 1048    *  Draws a rectangle in a canvas context, using coordinates
2303 02 Apr 14 olle 1049    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 1050    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 1051    *  Rectangle input is given as coordinates for lower left corner
2303 02 Apr 14 olle 1052    *  and rectangle width and height. 
2303 02 Apr 14 olle 1053    */
2303 02 Apr 14 olle 1054   function drawRect(x, y, rect_wdt, rect_hgt, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 1055   {
2303 02 Apr 14 olle 1056     var x_with_offset = fixCoordinate(context, x + xoff);
2303 02 Apr 14 olle 1057     var y_with_offset = fixCoordinate(context, yoff + hgt - y - rect_hgt);
2303 02 Apr 14 olle 1058     // Draw box rectangle
2303 02 Apr 14 olle 1059     context.strokeStyle = "#000000";
2303 02 Apr 14 olle 1060     context.strokeRect(x_with_offset, y_with_offset, Math.floor(rect_wdt), Math.floor(rect_hgt));
2303 02 Apr 14 olle 1061   }
2303 02 Apr 14 olle 1062    
2303 02 Apr 14 olle 1063   /**
2303 02 Apr 14 olle 1064    *  Draws a line in a canvas context, using coordinates
2303 02 Apr 14 olle 1065    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 1066    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 1067    */
2303 02 Apr 14 olle 1068   function drawLine(x1, y1, x2, y2, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 1069   {
2303 02 Apr 14 olle 1070     var x1_with_offset = fixCoordinate(context, x1 + xoff);
2303 02 Apr 14 olle 1071     var x2_with_offset = fixCoordinate(context, x2 + xoff);
2303 02 Apr 14 olle 1072     var y1_with_offset = fixCoordinate(context, yoff + hgt - y1);
2303 02 Apr 14 olle 1073     var y2_with_offset = fixCoordinate(context, yoff + hgt - y2);
2303 02 Apr 14 olle 1074     context.moveTo(x1_with_offset, y1_with_offset);
2303 02 Apr 14 olle 1075     context.lineTo(x2_with_offset, y2_with_offset);
2303 02 Apr 14 olle 1076   }
2303 02 Apr 14 olle 1077   
2303 02 Apr 14 olle 1078   /**
2303 02 Apr 14 olle 1079    *  Draws a dashed vertical line in a canvas context, using coordinates
2303 02 Apr 14 olle 1080    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 1081    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 1082    */
2303 02 Apr 14 olle 1083   function drawDashedLineVertical(x, y1, y2, context, xoff, yoff, wdt, hgt, dash_size, space_size)
2303 02 Apr 14 olle 1084   {
2303 02 Apr 14 olle 1085     var sign = 1;
2303 02 Apr 14 olle 1086     if (y1 > y2)
2303 02 Apr 14 olle 1087     {
2303 02 Apr 14 olle 1088       sign = -1;
2303 02 Apr 14 olle 1089     }
2303 02 Apr 14 olle 1090     var y = y1;
2303 02 Apr 14 olle 1091     var y_new;
2303 02 Apr 14 olle 1092     while (sign*(y2 - y) > 0)
2303 02 Apr 14 olle 1093     {
2303 02 Apr 14 olle 1094       y_new = y + sign*dash_size;
2303 02 Apr 14 olle 1095       if (sign*y_new > sign*y2)
2303 02 Apr 14 olle 1096       {
2303 02 Apr 14 olle 1097         y_new = y2;
2303 02 Apr 14 olle 1098       }
2303 02 Apr 14 olle 1099       drawLine(x, y, x, y_new, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 1100       y = y_new + sign*space_size;
2303 02 Apr 14 olle 1101     }
2303 02 Apr 14 olle 1102   }
2303 02 Apr 14 olle 1103    
2303 02 Apr 14 olle 1104   /**
2303 02 Apr 14 olle 1105    *  Draws a dotted horizontal line in a canvas context, using coordinates
2303 02 Apr 14 olle 1106    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 1107    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 1108    */
2303 02 Apr 14 olle 1109   function drawDottedLineHorizontal(x1, x2, y, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 1110   {
2303 02 Apr 14 olle 1111     var dash_step1 = 2;
2303 02 Apr 14 olle 1112     var space_step1 = 4;
2303 02 Apr 14 olle 1113     var sign = 1;
2303 02 Apr 14 olle 1114     if (x1 > x2)
2303 02 Apr 14 olle 1115     {
2303 02 Apr 14 olle 1116       sign = -1;
2303 02 Apr 14 olle 1117     }
2303 02 Apr 14 olle 1118     var x = x1;
2303 02 Apr 14 olle 1119     var x_new;
2303 02 Apr 14 olle 1120     while (sign*(x2 - x) > 0)
2303 02 Apr 14 olle 1121     {
2303 02 Apr 14 olle 1122       x_new = x + sign*dash_step1;
2303 02 Apr 14 olle 1123       if (sign*x_new > sign*x2)
2303 02 Apr 14 olle 1124       {
2303 02 Apr 14 olle 1125         x_new = x2;
2303 02 Apr 14 olle 1126       }
2303 02 Apr 14 olle 1127       drawLine(x, y, x_new, y, context, xoff, yoff, wdt, hgt);
2303 02 Apr 14 olle 1128       x = x_new + sign*space_step1;
2303 02 Apr 14 olle 1129     }
2303 02 Apr 14 olle 1130   }
2303 02 Apr 14 olle 1131    
2303 02 Apr 14 olle 1132   /**
2401 06 May 14 olle 1133    *  Draws a dotted vertical line in a canvas context, using coordinates
2401 06 May 14 olle 1134    *  where x increases to the right and y increases upwards,
2401 06 May 14 olle 1135    *  and where the offsets are added automatically.
2401 06 May 14 olle 1136    */
2401 06 May 14 olle 1137   function drawDottedLineVertical(x, y1, y2, context, xoff, yoff, wdt, hgt)
2401 06 May 14 olle 1138   {
2401 06 May 14 olle 1139     var dash_step1 = 2;
2401 06 May 14 olle 1140     var space_step1 = 4;
2401 06 May 14 olle 1141     drawDashedLineVertical(x, y1, y2, context, xoff, yoff, wdt, hgt, dash_step1, space_step1);
2401 06 May 14 olle 1142   }
2401 06 May 14 olle 1143    
2401 06 May 14 olle 1144   /**
2401 06 May 14 olle 1145    *  Draws a point in a canvas context, using coordinates
2401 06 May 14 olle 1146    *  where x increases to the right and y increases upwards,
2401 06 May 14 olle 1147    *  and where the offsets are added automatically.
2401 06 May 14 olle 1148    */
2401 06 May 14 olle 1149   function drawPoint(x, y, context, xoff, yoff, wdt, hgt)
2401 06 May 14 olle 1150   {
2401 06 May 14 olle 1151     var x_with_offset = fixCoordinate(context, x + xoff);
2401 06 May 14 olle 1152     var y_with_offset = fixCoordinate(context, yoff + hgt - y);
2401 06 May 14 olle 1153     // Draw a point as a rectangle with sides 1 pixel in length
2401 06 May 14 olle 1154     context.fillRect(x_with_offset, y_with_offset, 1, 1);
2401 06 May 14 olle 1155   }
2401 06 May 14 olle 1156   
2401 06 May 14 olle 1157   /**
2303 02 Apr 14 olle 1158    *  Draws text in a canvas context, using coordinates
2303 02 Apr 14 olle 1159    *  where x increases to the right and y increases upwards,
2303 02 Apr 14 olle 1160    *  and where the offsets are added automatically.
2303 02 Apr 14 olle 1161    */
2303 02 Apr 14 olle 1162   function drawText(text, x1, y1, context, xoff, yoff, wdt, hgt)
2303 02 Apr 14 olle 1163   {
2303 02 Apr 14 olle 1164     var x1_with_offset = fixCoordinate(context, x1 + xoff);
2303 02 Apr 14 olle 1165     var y1_with_offset = fixCoordinate(context, yoff + hgt - y1);
2303 02 Apr 14 olle 1166     context.fillText(text, x1_with_offset, y1_with_offset);
2303 02 Apr 14 olle 1167   }
2303 02 Apr 14 olle 1168
2303 02 Apr 14 olle 1169   /**
2303 02 Apr 14 olle 1170    *  Calculates an array of values for y-axis scale markers (ticks),
2303 02 Apr 14 olle 1171    *  given the min and max values of the data, and the numbers
2303 02 Apr 14 olle 1172    *  of markers (optional).
2303 02 Apr 14 olle 1173    *
2303 02 Apr 14 olle 1174    *  Based on PHP example on
2303 02 Apr 14 olle 1175    *  http://stackoverflow.com/questions/326679/choosing-an-attractive-linear-scale-for-a-graphs-y-axis
2303 02 Apr 14 olle 1176    *  but with modified behavior for step size < 1.
2303 02 Apr 14 olle 1177    */
2303 02 Apr 14 olle 1178   function calculateScaleMarkerArray(minDataValue, maxDataValue, numTicks)
2303 02 Apr 14 olle 1179   {
2303 02 Apr 14 olle 1180     if (numTicks == null || numTicks <= 0)
2303 02 Apr 14 olle 1181     {
2303 02 Apr 14 olle 1182       numTicks = 10;
2303 02 Apr 14 olle 1183     }
2303 02 Apr 14 olle 1184     // Adjust ticks if needed
2303 02 Apr 14 olle 1185     if (numTicks < 2)
2303 02 Apr 14 olle 1186     {
2303 02 Apr 14 olle 1187       numTicks = 2;
2303 02 Apr 14 olle 1188     }
2303 02 Apr 14 olle 1189     else if (numTicks > 2)
2303 02 Apr 14 olle 1190     {
2303 02 Apr 14 olle 1191       numTicks -= 2;
2303 02 Apr 14 olle 1192     }
2303 02 Apr 14 olle 1193
2303 02 Apr 14 olle 1194     // If min and max are identical, then adjust these values.
2303 02 Apr 14 olle 1195     if (minDataValue == maxDataValue)
2303 02 Apr 14 olle 1196     {
2303 02 Apr 14 olle 1197       minDataValue = minDataValue - 10;
2303 02 Apr 14 olle 1198       maxDataValue = maxDataValue + 10;
2303 02 Apr 14 olle 1199     }
2303 02 Apr 14 olle 1200
2303 02 Apr 14 olle 1201     // Determine range
2303 02 Apr 14 olle 1202     var range = maxDataValue - minDataValue;
2303 02 Apr 14 olle 1203     // Get raw step value
2303 02 Apr 14 olle 1204     var tempStep = range/numTicks;
2303 02 Apr 14 olle 1205     // Calculate pretty step value
2303 02 Apr 14 olle 1206     var mag = Math.floor(Math.log(tempStep)/Math.log(10) + 0.001);
2303 02 Apr 14 olle 1207     var magPow = Math.pow(10, mag);
2303 02 Apr 14 olle 1208     if (magPow >= 1)
2303 02 Apr 14 olle 1209     {
2303 02 Apr 14 olle 1210       magPow = Math.floor(magPow + 0.001);
2303 02 Apr 14 olle 1211     }
2303 02 Apr 14 olle 1212     var magMsd = Math.floor(tempStep/magPow + 0.5);
2303 02 Apr 14 olle 1213     var stepSize = magMsd*magPow;
2303 02 Apr 14 olle 1214     // Correction for stepSize < 1000
2303 02 Apr 14 olle 1215     if (stepSize < 1000)
2303 02 Apr 14 olle 1216     {
2303 02 Apr 14 olle 1217       if (stepSize >= 750)
2303 02 Apr 14 olle 1218       {
2303 02 Apr 14 olle 1219         stepSize = 1000;
2303 02 Apr 14 olle 1220       }
2303 02 Apr 14 olle 1221       else if (stepSize >= 250)
2303 02 Apr 14 olle 1222       {
2303 02 Apr 14 olle 1223         stepSize = 500;
2303 02 Apr 14 olle 1224       }
2303 02 Apr 14 olle 1225       else if (stepSize >= 75)
2303 02 Apr 14 olle 1226       {
2303 02 Apr 14 olle 1227         stepSize = 100;
2303 02 Apr 14 olle 1228       }
2303 02 Apr 14 olle 1229       else if (stepSize >= 25)
2303 02 Apr 14 olle 1230       {
2303 02 Apr 14 olle 1231         stepSize = 50;
2303 02 Apr 14 olle 1232       }
2303 02 Apr 14 olle 1233       else if (stepSize >= 7.5)
2303 02 Apr 14 olle 1234       {
2303 02 Apr 14 olle 1235         stepSize = 10;
2303 02 Apr 14 olle 1236       }
2303 02 Apr 14 olle 1237       else if (stepSize >= 2.5)
2303 02 Apr 14 olle 1238       {
2303 02 Apr 14 olle 1239         stepSize = 5;
2303 02 Apr 14 olle 1240       }
2303 02 Apr 14 olle 1241       else if (stepSize >= 0.75)
2303 02 Apr 14 olle 1242       {
2303 02 Apr 14 olle 1243         stepSize = 1;
2303 02 Apr 14 olle 1244       }
2303 02 Apr 14 olle 1245       else if (stepSize >= 0.25)
2303 02 Apr 14 olle 1246       {
2303 02 Apr 14 olle 1247         stepSize = 0.5;
2303 02 Apr 14 olle 1248       }
2303 02 Apr 14 olle 1249       else if (stepSize >= 0.075)
2303 02 Apr 14 olle 1250       {
2303 02 Apr 14 olle 1251         stepSize = 0.1;
2303 02 Apr 14 olle 1252       }
2303 02 Apr 14 olle 1253       else if (stepSize >= 0.025)
2303 02 Apr 14 olle 1254       {
2303 02 Apr 14 olle 1255         stepSize = 0.05;
2303 02 Apr 14 olle 1256       }
2303 02 Apr 14 olle 1257       else if (stepSize >= 0.0075)
2303 02 Apr 14 olle 1258       {
2303 02 Apr 14 olle 1259         stepSize = 0.01;
2303 02 Apr 14 olle 1260       }
2303 02 Apr 14 olle 1261       else if (stepSize >= 0.0025)
2303 02 Apr 14 olle 1262       {
2303 02 Apr 14 olle 1263         stepSize = 0.005;
2303 02 Apr 14 olle 1264       }
2303 02 Apr 14 olle 1265       else if (stepSize >= 0.00075)
2303 02 Apr 14 olle 1266       {
2303 02 Apr 14 olle 1267         stepSize = 0.001;
2303 02 Apr 14 olle 1268       }
2303 02 Apr 14 olle 1269       else if (stepSize >= 0.00025)
2303 02 Apr 14 olle 1270       {
2303 02 Apr 14 olle 1271         stepSize = 0.0005;
2303 02 Apr 14 olle 1272       }
2303 02 Apr 14 olle 1273       else if (stepSize >= 0.000075)
2303 02 Apr 14 olle 1274       {
2303 02 Apr 14 olle 1275         stepSize = 0.0001;
2303 02 Apr 14 olle 1276       }
2303 02 Apr 14 olle 1277     }
2303 02 Apr 14 olle 1278     
2303 02 Apr 14 olle 1279     // Build y label array
2303 02 Apr 14 olle 1280     // Lower and upper bounds calculations
2303 02 Apr 14 olle 1281     var lowerBound = stepSize*Math.floor(minDataValue/stepSize);
2303 02 Apr 14 olle 1282     var upperBound = stepSize*Math.ceil(maxDataValue/stepSize);
2303 02 Apr 14 olle 1283     // Build array
2303 02 Apr 14 olle 1284     var tickValuesArr = [];
2303 02 Apr 14 olle 1285     var value = lowerBound;
2303 02 Apr 14 olle 1286     var i = 0;
2303 02 Apr 14 olle 1287     tickValuesArr[i] = value;
2303 02 Apr 14 olle 1288     value += stepSize;
2303 02 Apr 14 olle 1289     while(value <= upperBound)
2303 02 Apr 14 olle 1290     {
2303 02 Apr 14 olle 1291       i++;
2303 02 Apr 14 olle 1292       tickValuesArr[i] = value;
2303 02 Apr 14 olle 1293       value += stepSize;
2303 02 Apr 14 olle 1294     }
2303 02 Apr 14 olle 1295     return tickValuesArr;
2303 02 Apr 14 olle 1296   }
2303 02 Apr 14 olle 1297    
2303 02 Apr 14 olle 1298   /**
2303 02 Apr 14 olle 1299    *  Trims an array of values for y-axis scale markers (ticks),
2303 02 Apr 14 olle 1300    *  to only include values in given value range.
2303 02 Apr 14 olle 1301    */
2303 02 Apr 14 olle 1302   function trimScaleMarkerArray(origTickValuesArr, minValue, maxValue)
2303 02 Apr 14 olle 1303   {
2303 02 Apr 14 olle 1304     // Build trimmed y label array
2303 02 Apr 14 olle 1305     var tickValuesArr = [];
2303 02 Apr 14 olle 1306     var value;
2303 02 Apr 14 olle 1307     var k = 0;
2303 02 Apr 14 olle 1308     for (i=0; i < origTickValuesArr.length; i++)
2303 02 Apr 14 olle 1309     {
2303 02 Apr 14 olle 1310       value = origTickValuesArr[i];
2303 02 Apr 14 olle 1311       if (value >= minValue && value <= maxValue)
2303 02 Apr 14 olle 1312       {
2303 02 Apr 14 olle 1313         // Add value to trimmed array
2303 02 Apr 14 olle 1314         tickValuesArr[k] = value;
2303 02 Apr 14 olle 1315         k++;
2303 02 Apr 14 olle 1316       }
2303 02 Apr 14 olle 1317     }
2303 02 Apr 14 olle 1318     return tickValuesArr;
2303 02 Apr 14 olle 1319   }
2303 02 Apr 14 olle 1320   
2303 02 Apr 14 olle 1321   /*
2303 02 Apr 14 olle 1322     Fixes a plotting coordinate by aligning it so that anti-aliasing
2303 02 Apr 14 olle 1323     is avoided. For 1-pixel line widths the coordate should alway be a 
2303 02 Apr 14 olle 1324     half-value, eg. 1.5, 10.5, etc.
2303 02 Apr 14 olle 1325   */
2303 02 Apr 14 olle 1326   function fixCoordinate(context, value)
2303 02 Apr 14 olle 1327   {
2303 02 Apr 14 olle 1328     return Math.floor(value) + (context.lineWidth % 2) / 2;
2303 02 Apr 14 olle 1329   }
2989 02 Dec 14 olle 1330
2989 02 Dec 14 olle 1331   /**
2989 02 Dec 14 olle 1332    *  Returns value for a JSON object given a key.
2989 02 Dec 14 olle 1333    *  If the JSON object or the value for the key is `null`,
2989 02 Dec 14 olle 1334    *  an empty string is returned.
2989 02 Dec 14 olle 1335    */
2989 02 Dec 14 olle 1336   function getJSONData(jsonObject, key)
2989 02 Dec 14 olle 1337   {
2989 02 Dec 14 olle 1338     var data = labenvdbstat.getJSONData(jsonObject, key, '');
2989 02 Dec 14 olle 1339     return data;
2989 02 Dec 14 olle 1340   }
2989 02 Dec 14 olle 1341
2989 02 Dec 14 olle 1342   /**
2989 02 Dec 14 olle 1343    *  Returns value for a JSON object given a key.
2989 02 Dec 14 olle 1344    *  If the JSON object or the value for the key is `null`,
2989 02 Dec 14 olle 1345    *  an the given default choice string is returned.
2989 02 Dec 14 olle 1346    */
2989 02 Dec 14 olle 1347   function getJSONData(jsonObject, key, defaultChoice)
2989 02 Dec 14 olle 1348   {
2989 02 Dec 14 olle 1349     var data = defaultChoice;
2989 02 Dec 14 olle 1350     if (jsonObject != null)
2989 02 Dec 14 olle 1351     {
2989 02 Dec 14 olle 1352       if (jsonObject[key] != null)
2989 02 Dec 14 olle 1353       {
2989 02 Dec 14 olle 1354         data = jsonObject[key];
2989 02 Dec 14 olle 1355       }
2989 02 Dec 14 olle 1356     }
2989 02 Dec 14 olle 1357     return data;
2989 02 Dec 14 olle 1358   }
2989 02 Dec 14 olle 1359
2303 02 Apr 14 olle 1360