1/** 2## jquery.flot.drawSeries.js 3 4This 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 defaultBottom = 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] : defaultBottom; 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);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.