src/pyams_skin/resources/js/ext/flot/jquery.flot.drawSeries.js
changeset 566 a1707c607eec
parent 565 318533413200
child 567 bca1726b1d85
equal deleted inserted replaced
565:318533413200 566:a1707c607eec
     1 /**
       
     2 ## jquery.flot.drawSeries.js
       
     3 
       
     4 This plugin is used by flot for drawing lines, plots, bars or area.
       
     5 
       
     6 ### Public methods
       
     7 */
       
     8 
       
     9 (function($) {
       
    10     "use strict";
       
    11 
       
    12     function DrawSeries() {
       
    13         function plotLine(datapoints, xoffset, yoffset, axisx, axisy, ctx, steps) {
       
    14             var points = datapoints.points,
       
    15                 ps = datapoints.pointsize,
       
    16                 prevx = null,
       
    17                 prevy = null;
       
    18             var x1 = 0.0,
       
    19                 y1 = 0.0,
       
    20                 x2 = 0.0,
       
    21                 y2 = 0.0,
       
    22                 mx = null,
       
    23                 my = null,
       
    24                 i = 0;
       
    25 
       
    26             ctx.beginPath();
       
    27             for (i = ps; i < points.length; i += ps) {
       
    28                 x1 = points[i - ps];
       
    29                 y1 = points[i - ps + 1];
       
    30                 x2 = points[i];
       
    31                 y2 = points[i + 1];
       
    32 
       
    33                 if (x1 === null || x2 === null) {
       
    34                     mx = null;
       
    35                     my = null;
       
    36                     continue;
       
    37                 }
       
    38 
       
    39                 if (isNaN(x1) || isNaN(x2) || isNaN(y1) || isNaN(y2)) {
       
    40                     prevx = null;
       
    41                     prevy = null;
       
    42                     continue;
       
    43                 }
       
    44 
       
    45                 if(steps){
       
    46                     if (mx !== null && my !== null) {
       
    47                         // if middle point exists, transfer p2 -> p1 and p1 -> mp
       
    48                         x2 = x1;
       
    49                         y2 = y1;
       
    50                         x1 = mx;
       
    51                         y1 = my;
       
    52 
       
    53                         // 'remove' middle point
       
    54                         mx = null;
       
    55                         my = null;
       
    56 
       
    57                         // subtract pointsize from i to have current point p1 handled again
       
    58                         i -= ps;
       
    59                     } else if (y1 !== y2 && x1 !== x2) {
       
    60                         // create a middle point
       
    61                         y2 = y1;
       
    62                         mx = x2;
       
    63                         my = y1;
       
    64                     }
       
    65                 }
       
    66 
       
    67                 // clip with ymin
       
    68                 if (y1 <= y2 && y1 < axisy.min) {
       
    69                     if (y2 < axisy.min) {
       
    70                         // line segment is outside
       
    71                         continue;
       
    72                     }
       
    73                     // compute new intersection point
       
    74                     x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
       
    75                     y1 = axisy.min;
       
    76                 } else if (y2 <= y1 && y2 < axisy.min) {
       
    77                     if (y1 < axisy.min) {
       
    78                         continue;
       
    79                     }
       
    80 
       
    81                     x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
       
    82                     y2 = axisy.min;
       
    83                 }
       
    84 
       
    85                 // clip with ymax
       
    86                 if (y1 >= y2 && y1 > axisy.max) {
       
    87                     if (y2 > axisy.max) {
       
    88                         continue;
       
    89                     }
       
    90 
       
    91                     x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
       
    92                     y1 = axisy.max;
       
    93                 } else if (y2 >= y1 && y2 > axisy.max) {
       
    94                     if (y1 > axisy.max) {
       
    95                         continue;
       
    96                     }
       
    97 
       
    98                     x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
       
    99                     y2 = axisy.max;
       
   100                 }
       
   101 
       
   102                 // clip with xmin
       
   103                 if (x1 <= x2 && x1 < axisx.min) {
       
   104                     if (x2 < axisx.min) {
       
   105                         continue;
       
   106                     }
       
   107 
       
   108                     y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
       
   109                     x1 = axisx.min;
       
   110                 } else if (x2 <= x1 && x2 < axisx.min) {
       
   111                     if (x1 < axisx.min) {
       
   112                         continue;
       
   113                     }
       
   114 
       
   115                     y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
       
   116                     x2 = axisx.min;
       
   117                 }
       
   118 
       
   119                 // clip with xmax
       
   120                 if (x1 >= x2 && x1 > axisx.max) {
       
   121                     if (x2 > axisx.max) {
       
   122                         continue;
       
   123                     }
       
   124 
       
   125                     y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
       
   126                     x1 = axisx.max;
       
   127                 } else if (x2 >= x1 && x2 > axisx.max) {
       
   128                     if (x1 > axisx.max) {
       
   129                         continue;
       
   130                     }
       
   131 
       
   132                     y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
       
   133                     x2 = axisx.max;
       
   134                 }
       
   135 
       
   136                 if (x1 !== prevx || y1 !== prevy) {
       
   137                     ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);
       
   138                 }
       
   139 
       
   140                 prevx = x2;
       
   141                 prevy = y2;
       
   142                 ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);
       
   143             }
       
   144             ctx.stroke();
       
   145         }
       
   146 
       
   147         function plotLineArea(datapoints, axisx, axisy, fillTowards, ctx, steps) {
       
   148             var points = datapoints.points,
       
   149                 ps = datapoints.pointsize,
       
   150                 bottom = fillTowards > axisy.min ? Math.min(axisy.max, fillTowards) : axisy.min,
       
   151                 i = 0,
       
   152                 ypos = 1,
       
   153                 areaOpen = false,
       
   154                 segmentStart = 0,
       
   155                 segmentEnd = 0,
       
   156                 mx = null,
       
   157                 my = null;
       
   158 
       
   159             // we process each segment in two turns, first forward
       
   160             // direction to sketch out top, then once we hit the
       
   161             // end we go backwards to sketch the bottom
       
   162             while (true) {
       
   163                 if (ps > 0 && i > points.length + ps) {
       
   164                     break;
       
   165                 }
       
   166 
       
   167                 i += ps; // ps is negative if going backwards
       
   168 
       
   169                 var x1 = points[i - ps],
       
   170                     y1 = points[i - ps + ypos],
       
   171                     x2 = points[i],
       
   172                     y2 = points[i + ypos];
       
   173 
       
   174                 if (ps === -2) {
       
   175                     /* going backwards and no value for the bottom provided in the series*/
       
   176                     y1 = y2 = bottom;
       
   177                 }
       
   178 
       
   179                 if (areaOpen) {
       
   180                     if (ps > 0 && x1 != null && x2 == null) {
       
   181                         // at turning point
       
   182                         segmentEnd = i;
       
   183                         ps = -ps;
       
   184                         ypos = 2;
       
   185                         continue;
       
   186                     }
       
   187 
       
   188                     if (ps < 0 && i === segmentStart + ps) {
       
   189                         // done with the reverse sweep
       
   190                         ctx.fill();
       
   191                         areaOpen = false;
       
   192                         ps = -ps;
       
   193                         ypos = 1;
       
   194                         i = segmentStart = segmentEnd + ps;
       
   195                         continue;
       
   196                     }
       
   197                 }
       
   198 
       
   199                 if (x1 == null || x2 == null) {
       
   200                     mx = null;
       
   201                     my = null;
       
   202                     continue;
       
   203                 }
       
   204 
       
   205                 if(steps){
       
   206                     if (mx !== null && my !== null) {
       
   207                         // if middle point exists, transfer p2 -> p1 and p1 -> mp
       
   208                         x2 = x1;
       
   209                         y2 = y1;
       
   210                         x1 = mx;
       
   211                         y1 = my;
       
   212 
       
   213                         // 'remove' middle point
       
   214                         mx = null;
       
   215                         my = null;
       
   216 
       
   217                         // subtract pointsize from i to have current point p1 handled again
       
   218                         i -= ps;
       
   219                     } else if (y1 !== y2 && x1 !== x2) {
       
   220                         // create a middle point
       
   221                         y2 = y1;
       
   222                         mx = x2;
       
   223                         my = y1;
       
   224                     }
       
   225                 }
       
   226 
       
   227                 // clip x values
       
   228 
       
   229                 // clip with xmin
       
   230                 if (x1 <= x2 && x1 < axisx.min) {
       
   231                     if (x2 < axisx.min) {
       
   232                         continue;
       
   233                     }
       
   234 
       
   235                     y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
       
   236                     x1 = axisx.min;
       
   237                 } else if (x2 <= x1 && x2 < axisx.min) {
       
   238                     if (x1 < axisx.min) {
       
   239                         continue;
       
   240                     }
       
   241 
       
   242                     y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
       
   243                     x2 = axisx.min;
       
   244                 }
       
   245 
       
   246                 // clip with xmax
       
   247                 if (x1 >= x2 && x1 > axisx.max) {
       
   248                     if (x2 > axisx.max) {
       
   249                         continue;
       
   250                     }
       
   251 
       
   252                     y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
       
   253                     x1 = axisx.max;
       
   254                 } else if (x2 >= x1 && x2 > axisx.max) {
       
   255                     if (x1 > axisx.max) {
       
   256                         continue;
       
   257                     }
       
   258 
       
   259                     y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
       
   260                     x2 = axisx.max;
       
   261                 }
       
   262 
       
   263                 if (!areaOpen) {
       
   264                     // open area
       
   265                     ctx.beginPath();
       
   266                     ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
       
   267                     areaOpen = true;
       
   268                 }
       
   269 
       
   270                 // now first check the case where both is outside
       
   271                 if (y1 >= axisy.max && y2 >= axisy.max) {
       
   272                     ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));
       
   273                     ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max));
       
   274                     continue;
       
   275                 } else if (y1 <= axisy.min && y2 <= axisy.min) {
       
   276                     ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min));
       
   277                     ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));
       
   278                     continue;
       
   279                 }
       
   280 
       
   281                 // else it's a bit more complicated, there might
       
   282                 // be a flat maxed out rectangle first, then a
       
   283                 // triangular cutout or reverse; to find these
       
   284                 // keep track of the current x values
       
   285                 var x1old = x1,
       
   286                     x2old = x2;
       
   287 
       
   288                 // clip the y values, without shortcutting, we
       
   289                 // go through all cases in turn
       
   290 
       
   291                 // clip with ymin
       
   292                 if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) {
       
   293                     x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
       
   294                     y1 = axisy.min;
       
   295                 } else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) {
       
   296                     x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
       
   297                     y2 = axisy.min;
       
   298                 }
       
   299 
       
   300                 // clip with ymax
       
   301                 if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) {
       
   302                     x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
       
   303                     y1 = axisy.max;
       
   304                 } else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) {
       
   305                     x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
       
   306                     y2 = axisy.max;
       
   307                 }
       
   308 
       
   309                 // if the x value was changed we got a rectangle
       
   310                 // to fill
       
   311                 if (x1 !== x1old) {
       
   312                     ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1));
       
   313                     // it goes to (x1, y1), but we fill that below
       
   314                 }
       
   315 
       
   316                 // fill triangular section, this sometimes result
       
   317                 // in redundant points if (x1, y1) hasn't changed
       
   318                 // from previous line to, but we just ignore that
       
   319                 ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));
       
   320                 ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
       
   321 
       
   322                 // fill the other rectangle if it's there
       
   323                 if (x2 !== x2old) {
       
   324                     ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
       
   325                     ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2));
       
   326                 }
       
   327             }
       
   328         }
       
   329 
       
   330         /**
       
   331         - drawSeriesLines(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient)
       
   332 
       
   333          This function is used for drawing lines or area fill.  In case the series has line decimation function
       
   334          attached, before starting to draw, as an optimization the points will first be decimated.
       
   335 
       
   336          The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and
       
   337          plotHeight are the corresponding parameters of flot used to determine the drawing surface.
       
   338          The function getColorOrGradient is used to compute the fill style of lines and area.
       
   339         */
       
   340         function drawSeriesLines(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) {
       
   341             ctx.save();
       
   342             ctx.translate(plotOffset.left, plotOffset.top);
       
   343             ctx.lineJoin = "round";
       
   344 
       
   345             if (series.lines.dashes && ctx.setLineDash) {
       
   346                 ctx.setLineDash(series.lines.dashes);
       
   347             }
       
   348 
       
   349             var datapoints = {
       
   350                 format: series.datapoints.format,
       
   351                 points: series.datapoints.points,
       
   352                 pointsize: series.datapoints.pointsize
       
   353             };
       
   354 
       
   355             if (series.decimate) {
       
   356                 datapoints.points = series.decimate(series, series.xaxis.min, series.xaxis.max, plotWidth, series.yaxis.min, series.yaxis.max, plotHeight);
       
   357             }
       
   358 
       
   359             var lw = series.lines.lineWidth;
       
   360 
       
   361             ctx.lineWidth = lw;
       
   362             ctx.strokeStyle = series.color;
       
   363             var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight, getColorOrGradient);
       
   364             if (fillStyle) {
       
   365                 ctx.fillStyle = fillStyle;
       
   366                 plotLineArea(datapoints, series.xaxis, series.yaxis, series.lines.fillTowards || 0, ctx, series.lines.steps);
       
   367             }
       
   368 
       
   369             if (lw > 0) {
       
   370                 plotLine(datapoints, 0, 0, series.xaxis, series.yaxis, ctx, series.lines.steps);
       
   371             }
       
   372 
       
   373             ctx.restore();
       
   374         }
       
   375 
       
   376         /**
       
   377         - drawSeriesPoints(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient)
       
   378 
       
   379          This function is used for drawing points using a given symbol. In case the series has points decimation
       
   380          function attached, before starting to draw, as an optimization the points will first be decimated.
       
   381 
       
   382          The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and
       
   383          plotHeight are the corresponding parameters of flot used to determine the drawing surface.
       
   384          The function drawSymbol is used to compute and draw the symbol chosen for the points.
       
   385         */
       
   386         function drawSeriesPoints(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) {
       
   387             function drawCircle(ctx, x, y, radius, shadow, fill) {
       
   388                 ctx.moveTo(x + radius, y);
       
   389                 ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
       
   390             }
       
   391             drawCircle.fill = true;
       
   392             function plotPoints(datapoints, radius, fill, offset, shadow, axisx, axisy, drawSymbolFn) {
       
   393                 var points = datapoints.points,
       
   394                     ps = datapoints.pointsize;
       
   395 
       
   396                 ctx.beginPath();
       
   397                 for (var i = 0; i < points.length; i += ps) {
       
   398                     var x = points[i],
       
   399                         y = points[i + 1];
       
   400                     if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) {
       
   401                         continue;
       
   402                     }
       
   403 
       
   404                     x = axisx.p2c(x);
       
   405                     y = axisy.p2c(y) + offset;
       
   406 
       
   407                     drawSymbolFn(ctx, x, y, radius, shadow, fill);
       
   408                 }
       
   409                 if (drawSymbolFn.fill && !shadow) {
       
   410                     ctx.fill();
       
   411                 }
       
   412                 ctx.stroke();
       
   413             }
       
   414 
       
   415             ctx.save();
       
   416             ctx.translate(plotOffset.left, plotOffset.top);
       
   417 
       
   418             var datapoints = {
       
   419                 format: series.datapoints.format,
       
   420                 points: series.datapoints.points,
       
   421                 pointsize: series.datapoints.pointsize
       
   422             };
       
   423 
       
   424             if (series.decimatePoints) {
       
   425                 datapoints.points = series.decimatePoints(series, series.xaxis.min, series.xaxis.max, plotWidth, series.yaxis.min, series.yaxis.max, plotHeight);
       
   426             }
       
   427 
       
   428             var lw = series.points.lineWidth,
       
   429                 radius = series.points.radius,
       
   430                 symbol = series.points.symbol,
       
   431                 drawSymbolFn;
       
   432 
       
   433             if (symbol === 'circle') {
       
   434                 drawSymbolFn = drawCircle;
       
   435             } else if (typeof symbol === 'string' && drawSymbol && drawSymbol[symbol]) {
       
   436                 drawSymbolFn = drawSymbol[symbol];
       
   437             } else if (typeof drawSymbol === 'function') {
       
   438                 drawSymbolFn = drawSymbol;
       
   439             }
       
   440 
       
   441             // If the user sets the line width to 0, we change it to a very
       
   442             // small value. A line width of 0 seems to force the default of 1.
       
   443 
       
   444             if (lw === 0) {
       
   445                 lw = 0.0001;
       
   446             }
       
   447 
       
   448             ctx.lineWidth = lw;
       
   449             ctx.fillStyle = getFillStyle(series.points, series.color, null, null, getColorOrGradient);
       
   450             ctx.strokeStyle = series.color;
       
   451             plotPoints(datapoints, radius,
       
   452                 true, 0, false,
       
   453                 series.xaxis, series.yaxis, drawSymbolFn);
       
   454             ctx.restore();
       
   455         }
       
   456 
       
   457         function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) {
       
   458             var left = x + barLeft,
       
   459                 right = x + barRight,
       
   460                 bottom = b, top = y,
       
   461                 drawLeft, drawRight, drawTop, drawBottom = false,
       
   462                 tmp;
       
   463 
       
   464             drawLeft = drawRight = drawTop = true;
       
   465 
       
   466             // in horizontal mode, we start the bar from the left
       
   467             // instead of from the bottom so it appears to be
       
   468             // horizontal rather than vertical
       
   469             if (horizontal) {
       
   470                 drawBottom = drawRight = drawTop = true;
       
   471                 drawLeft = false;
       
   472                 left = b;
       
   473                 right = x;
       
   474                 top = y + barLeft;
       
   475                 bottom = y + barRight;
       
   476 
       
   477                 // account for negative bars
       
   478                 if (right < left) {
       
   479                     tmp = right;
       
   480                     right = left;
       
   481                     left = tmp;
       
   482                     drawLeft = true;
       
   483                     drawRight = false;
       
   484                 }
       
   485             }
       
   486             else {
       
   487                 drawLeft = drawRight = drawTop = true;
       
   488                 drawBottom = false;
       
   489                 left = x + barLeft;
       
   490                 right = x + barRight;
       
   491                 bottom = b;
       
   492                 top = y;
       
   493 
       
   494                 // account for negative bars
       
   495                 if (top < bottom) {
       
   496                     tmp = top;
       
   497                     top = bottom;
       
   498                     bottom = tmp;
       
   499                     drawBottom = true;
       
   500                     drawTop = false;
       
   501                 }
       
   502             }
       
   503 
       
   504             // clip
       
   505             if (right < axisx.min || left > axisx.max ||
       
   506                 top < axisy.min || bottom > axisy.max) {
       
   507                 return;
       
   508             }
       
   509 
       
   510             if (left < axisx.min) {
       
   511                 left = axisx.min;
       
   512                 drawLeft = false;
       
   513             }
       
   514 
       
   515             if (right > axisx.max) {
       
   516                 right = axisx.max;
       
   517                 drawRight = false;
       
   518             }
       
   519 
       
   520             if (bottom < axisy.min) {
       
   521                 bottom = axisy.min;
       
   522                 drawBottom = false;
       
   523             }
       
   524 
       
   525             if (top > axisy.max) {
       
   526                 top = axisy.max;
       
   527                 drawTop = false;
       
   528             }
       
   529 
       
   530             left = axisx.p2c(left);
       
   531             bottom = axisy.p2c(bottom);
       
   532             right = axisx.p2c(right);
       
   533             top = axisy.p2c(top);
       
   534 
       
   535             // fill the bar
       
   536             if (fillStyleCallback) {
       
   537                 c.fillStyle = fillStyleCallback(bottom, top);
       
   538                 c.fillRect(left, top, right - left, bottom - top)
       
   539             }
       
   540 
       
   541             // draw outline
       
   542             if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) {
       
   543                 c.beginPath();
       
   544 
       
   545                 // FIXME: inline moveTo is buggy with excanvas
       
   546                 c.moveTo(left, bottom);
       
   547                 if (drawLeft) {
       
   548                     c.lineTo(left, top);
       
   549                 } else {
       
   550                     c.moveTo(left, top);
       
   551                 }
       
   552 
       
   553                 if (drawTop) {
       
   554                     c.lineTo(right, top);
       
   555                 } else {
       
   556                     c.moveTo(right, top);
       
   557                 }
       
   558 
       
   559                 if (drawRight) {
       
   560                     c.lineTo(right, bottom);
       
   561                 } else {
       
   562                     c.moveTo(right, bottom);
       
   563                 }
       
   564 
       
   565                 if (drawBottom) {
       
   566                     c.lineTo(left, bottom);
       
   567                 } else {
       
   568                     c.moveTo(left, bottom);
       
   569                 }
       
   570 
       
   571                 c.stroke();
       
   572             }
       
   573         }
       
   574 
       
   575         /**
       
   576         - drawSeriesBars(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient)
       
   577 
       
   578          This function is used for drawing series represented as bars. In case the series has decimation
       
   579          function attached, before starting to draw, as an optimization the points will first be decimated.
       
   580 
       
   581          The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and
       
   582          plotHeight are the corresponding parameters of flot used to determine the drawing surface.
       
   583          The function getColorOrGradient is used to compute the fill style of bars.
       
   584         */
       
   585         function drawSeriesBars(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) {
       
   586             function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) {
       
   587                 var points = datapoints.points,
       
   588                     ps = datapoints.pointsize,
       
   589                     fillTowards = series.bars.fillTowards || 0,
       
   590                     calculatedBottom = fillTowards > axisy.min ? Math.min(axisy.max, fillTowards) : axisy.min;
       
   591 
       
   592                 for (var i = 0; i < points.length; i += ps) {
       
   593                     if (points[i] == null) {
       
   594                         continue;
       
   595                     }
       
   596 
       
   597                     // Use third point as bottom if pointsize is 3
       
   598                     var bottom = ps === 3 ? points[i + 2] : calculatedBottom;
       
   599                     drawBar(points[i], points[i + 1], bottom, barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth);
       
   600                 }
       
   601             }
       
   602 
       
   603             ctx.save();
       
   604             ctx.translate(plotOffset.left, plotOffset.top);
       
   605 
       
   606             var datapoints = {
       
   607                 format: series.datapoints.format,
       
   608                 points: series.datapoints.points,
       
   609                 pointsize: series.datapoints.pointsize
       
   610             };
       
   611 
       
   612             if (series.decimate) {
       
   613                 datapoints.points = series.decimate(series, series.xaxis.min, series.xaxis.max, plotWidth);
       
   614             }
       
   615 
       
   616             ctx.lineWidth = series.bars.lineWidth;
       
   617             ctx.strokeStyle = series.color;
       
   618 
       
   619             var barLeft;
       
   620             var barWidth = series.bars.barWidth[0] || series.bars.barWidth;
       
   621             switch (series.bars.align) {
       
   622                 case "left":
       
   623                     barLeft = 0;
       
   624                     break;
       
   625                 case "right":
       
   626                     barLeft = -barWidth;
       
   627                     break;
       
   628                 default:
       
   629                     barLeft = -barWidth / 2;
       
   630             }
       
   631 
       
   632             var fillStyleCallback = series.bars.fill ? function(bottom, top) {
       
   633                 return getFillStyle(series.bars, series.color, bottom, top, getColorOrGradient);
       
   634             } : null;
       
   635 
       
   636             plotBars(datapoints, barLeft, barLeft + barWidth, fillStyleCallback, series.xaxis, series.yaxis);
       
   637             ctx.restore();
       
   638         }
       
   639 
       
   640         function getFillStyle(filloptions, seriesColor, bottom, top, getColorOrGradient) {
       
   641             var fill = filloptions.fill;
       
   642             if (!fill) {
       
   643                 return null;
       
   644             }
       
   645 
       
   646             if (filloptions.fillColor) {
       
   647                 return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);
       
   648             }
       
   649 
       
   650             var c = $.color.parse(seriesColor);
       
   651             c.a = typeof fill === "number" ? fill : 0.4;
       
   652             c.normalize();
       
   653             return c.toString();
       
   654         }
       
   655 
       
   656         this.drawSeriesLines = drawSeriesLines;
       
   657         this.drawSeriesPoints = drawSeriesPoints;
       
   658         this.drawSeriesBars = drawSeriesBars;
       
   659         this.drawBar = drawBar;
       
   660     };
       
   661 
       
   662     $.plot.drawSeries = new DrawSeries();
       
   663 })(jQuery);