[ Index ] |
PHP Cross Reference of vtigercrm-6.1.0 |
[Summary view] [Print] [Text view]
1 /** 2 * jqPlot 3 * Pure JavaScript plotting plugin using jQuery 4 * 5 * Version: 1.0.2 6 * Revision: 1108 7 * 8 * Copyright (c) 2009-2011 Chris Leonello 9 * jqPlot is currently available for use in all personal or commercial projects 10 * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 11 * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 12 * choose the license that best suits your project and use it accordingly. 13 * 14 * Although not required, the author would appreciate an email letting him 15 * know of any substantial use of jqPlot. You can reach the author at: 16 * chris at jqplot dot com or see http://www.jqplot.com/info.php . 17 * 18 * If you are feeling kind and generous, consider supporting the project by 19 * making a donation at: http://www.jqplot.com/donate.php . 20 * 21 * sprintf functions contained in jqplot.sprintf.js by Ash Searle: 22 * 23 * version 2007.04.27 24 * author Ash Searle 25 * http://hexmen.com/blog/2007/03/printf-sprintf/ 26 * http://hexmen.com/js/sprintf.js 27 * The author (Ash Searle) has placed this code in the public domain: 28 * "This code is unrestricted: you are free to use it however you like." 29 * 30 */ 31 (function($) { 32 /** 33 * Class: $.jqplot.FunnelRenderer 34 * Plugin renderer to draw a funnel chart. 35 * x values, if present, will be used as labels. 36 * y values give area size. 37 * 38 * Funnel charts will draw a single series 39 * only. 40 * 41 * To use this renderer, you need to include the 42 * funnel renderer plugin, for example: 43 * 44 * > <script type="text/javascript" src="plugins/jqplot.funnelRenderer.js"></script> 45 * 46 * Properties described here are passed into the $.jqplot function 47 * as options on the series renderer. For example: 48 * 49 * > plot2 = $.jqplot('chart2', [s1, s2], { 50 * > seriesDefaults: { 51 * > renderer:$.jqplot.FunnelRenderer, 52 * > rendererOptions:{ 53 * > sectionMargin: 12, 54 * > widthRatio: 0.3 55 * > } 56 * > } 57 * > }); 58 * 59 * IMPORTANT 60 * 61 * *The funnel renderer will reorder data in descending order* so the largest value in 62 * the data set is first and displayed on top of the funnel. Data will then 63 * be displayed in descending order down the funnel. The area of each funnel 64 * section will correspond to the value of each data point relative to the sum 65 * of all values. That is section area is proportional to section value divided by 66 * sum of all section values. 67 * 68 * If your data is not in descending order when passed into the plot, *it will be 69 * reordered* when stored in the series.data property. A copy of the unordered 70 * data is kept in the series._unorderedData property. 71 * 72 * A funnel plot will trigger events on the plot target 73 * according to user interaction. All events return the event object, 74 * the series index, the point (section) index, and the point data for 75 * the appropriate section. *Note* the point index will referr to the ordered 76 * data, not the original unordered data. 77 * 78 * 'jqplotDataMouseOver' - triggered when mousing over a section. 79 * 'jqplotDataHighlight' - triggered the first time user mouses over a section, 80 * if highlighting is enabled. 81 * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of 82 * a highlighted section. 83 * 'jqplotDataClick' - triggered when the user clicks on a section. 84 * 'jqplotDataRightClick' - tiggered when the user right clicks on a section if 85 * the "captureRightClick" option is set to true on the plot. 86 */ 87 $.jqplot.FunnelRenderer = function(){ 88 $.jqplot.LineRenderer.call(this); 89 }; 90 91 $.jqplot.FunnelRenderer.prototype = new $.jqplot.LineRenderer(); 92 $.jqplot.FunnelRenderer.prototype.constructor = $.jqplot.FunnelRenderer; 93 94 // called with scope of a series 95 $.jqplot.FunnelRenderer.prototype.init = function(options, plot) { 96 // Group: Properties 97 // 98 // prop: padding 99 // padding between the funnel and plot edges, legend, etc. 100 this.padding = {top: 20, right: 20, bottom: 20, left: 20}; 101 // prop: sectionMargin 102 // spacing between funnel sections in pixels. 103 this.sectionMargin = 6; 104 // prop: fill 105 // true or false, wether to fill the areas. 106 this.fill = true; 107 // prop: shadowOffset 108 // offset of the shadow from the area and offset of 109 // each succesive stroke of the shadow from the last. 110 this.shadowOffset = 2; 111 // prop: shadowAlpha 112 // transparency of the shadow (0 = transparent, 1 = opaque) 113 this.shadowAlpha = 0.07; 114 // prop: shadowDepth 115 // number of strokes to apply to the shadow, 116 // each stroke offset shadowOffset from the last. 117 this.shadowDepth = 5; 118 // prop: highlightMouseOver 119 // True to highlight area when moused over. 120 // This must be false to enable highlightMouseDown to highlight when clicking on a area. 121 this.highlightMouseOver = true; 122 // prop: highlightMouseDown 123 // True to highlight when a mouse button is pressed over a area. 124 // This will be disabled if highlightMouseOver is true. 125 this.highlightMouseDown = false; 126 // prop: highlightColors 127 // array of colors to use when highlighting an area. 128 this.highlightColors = []; 129 // prop: widthRatio 130 // The ratio of the width of the top of the funnel to the bottom. 131 // a ratio of 0 will make an upside down pyramid. 132 this.widthRatio = 0.2; 133 // prop: lineWidth 134 // width of line if areas are stroked and not filled. 135 this.lineWidth = 2; 136 // prop: dataLabels 137 // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices. 138 // Defaults to percentage of each pie slice. 139 this.dataLabels = 'percent'; 140 // prop: showDataLabels 141 // true to show data labels on slices. 142 this.showDataLabels = false; 143 // prop: dataLabelFormatString 144 // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage. 145 this.dataLabelFormatString = null; 146 // prop: dataLabelThreshold 147 // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed. 148 // This applies to all label types, not just to percentage labels. 149 this.dataLabelThreshold = 3; 150 this._type = 'funnel'; 151 152 this.tickRenderer = $.jqplot.FunnelTickRenderer; 153 154 // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver 155 if (options.highlightMouseDown && options.highlightMouseOver == null) { 156 options.highlightMouseOver = false; 157 } 158 159 $.extend(true, this, options); 160 161 // index of the currenty highlighted point, if any 162 this._highlightedPoint = null; 163 164 // lengths of bases, or horizontal sides of areas of trapezoid. 165 this._bases = []; 166 // total area 167 this._atot; 168 // areas of segments. 169 this._areas = []; 170 // vertical lengths of segments. 171 this._lengths = []; 172 // angle of the funnel to vertical. 173 this._angle; 174 this._dataIndices = []; 175 176 // sort data 177 this._unorderedData = $.extend(true, [], this.data); 178 var idxs = $.extend(true, [], this.data); 179 for (var i=0; i<idxs.length; i++) { 180 idxs[i].push(i); 181 } 182 this.data.sort( function (a, b) { return b[1] - a[1]; } ); 183 idxs.sort( function (a, b) { return b[1] - a[1]; }); 184 for (var i=0; i<idxs.length; i++) { 185 this._dataIndices.push(idxs[i][2]); 186 } 187 188 // set highlight colors if none provided 189 if (this.highlightColors.length == 0) { 190 for (var i=0; i<this.seriesColors.length; i++){ 191 var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); 192 var newrgb = [rgba[0], rgba[1], rgba[2]]; 193 var sum = newrgb[0] + newrgb[1] + newrgb[2]; 194 for (var j=0; j<3; j++) { 195 // when darkening, lowest color component can be is 60. 196 newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.4 * (255 - newrgb[j]); 197 newrgb[j] = parseInt(newrgb[j], 10); 198 } 199 this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); 200 } 201 } 202 203 plot.postParseOptionsHooks.addOnce(postParseOptions); 204 plot.postInitHooks.addOnce(postInit); 205 plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); 206 plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); 207 plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); 208 plot.eventListenerHooks.addOnce('jqplotClick', handleClick); 209 plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); 210 plot.postDrawHooks.addOnce(postPlotDraw); 211 212 }; 213 214 // gridData will be of form [label, percentage of total] 215 $.jqplot.FunnelRenderer.prototype.setGridData = function(plot) { 216 // set gridData property. This will hold angle in radians of each data point. 217 var sum = 0; 218 var td = []; 219 for (var i=0; i<this.data.length; i++){ 220 sum += this.data[i][1]; 221 td.push([this.data[i][0], this.data[i][1]]); 222 } 223 224 // normalize y values, so areas are proportional. 225 for (var i=0; i<td.length; i++) { 226 td[i][1] = td[i][1]/sum; 227 } 228 229 this._bases = new Array(td.length + 1); 230 this._lengths = new Array(td.length); 231 232 this.gridData = td; 233 }; 234 235 $.jqplot.FunnelRenderer.prototype.makeGridData = function(data, plot) { 236 // set gridData property. This will hold angle in radians of each data point. 237 var sum = 0; 238 var td = []; 239 for (var i=0; i<this.data.length; i++){ 240 sum += this.data[i][1]; 241 td.push([this.data[i][0], this.data[i][1]]); 242 } 243 244 // normalize y values, so areas are proportional. 245 for (var i=0; i<td.length; i++) { 246 td[i][1] = td[i][1]/sum; 247 } 248 249 this._bases = new Array(td.length + 1); 250 this._lengths = new Array(td.length); 251 252 return td; 253 }; 254 255 $.jqplot.FunnelRenderer.prototype.drawSection = function (ctx, vertices, color, isShadow) { 256 var fill = this.fill; 257 var lineWidth = this.lineWidth; 258 ctx.save(); 259 260 if (isShadow) { 261 for (var i=0; i<this.shadowDepth; i++) { 262 ctx.save(); 263 ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); 264 doDraw(); 265 } 266 } 267 268 else { 269 doDraw(); 270 } 271 272 function doDraw () { 273 ctx.beginPath(); 274 ctx.fillStyle = color; 275 ctx.strokeStyle = color; 276 ctx.lineWidth = lineWidth; 277 ctx.moveTo(vertices[0][0], vertices[0][1]); 278 for (var i=1; i<4; i++) { 279 ctx.lineTo(vertices[i][0], vertices[i][1]); 280 } 281 ctx.closePath(); 282 if (fill) { 283 ctx.fill(); 284 } 285 else { 286 ctx.stroke(); 287 } 288 } 289 290 if (isShadow) { 291 for (var i=0; i<this.shadowDepth; i++) { 292 ctx.restore(); 293 } 294 } 295 296 ctx.restore(); 297 }; 298 299 // called with scope of series 300 $.jqplot.FunnelRenderer.prototype.draw = function (ctx, gd, options, plot) { 301 var i; 302 var opts = (options != undefined) ? options : {}; 303 // offset and direction of offset due to legend placement 304 var offx = 0; 305 var offy = 0; 306 var trans = 1; 307 this._areas = []; 308 // var colorGenerator = new this.colorGenerator(this.seriesColors); 309 if (options.legendInfo && options.legendInfo.placement == 'insideGrid') { 310 var li = options.legendInfo; 311 switch (li.location) { 312 case 'nw': 313 offx = li.width + li.xoffset; 314 break; 315 case 'w': 316 offx = li.width + li.xoffset; 317 break; 318 case 'sw': 319 offx = li.width + li.xoffset; 320 break; 321 case 'ne': 322 offx = li.width + li.xoffset; 323 trans = -1; 324 break; 325 case 'e': 326 offx = li.width + li.xoffset; 327 trans = -1; 328 break; 329 case 'se': 330 offx = li.width + li.xoffset; 331 trans = -1; 332 break; 333 case 'n': 334 offy = li.height + li.yoffset; 335 break; 336 case 's': 337 offy = li.height + li.yoffset; 338 trans = -1; 339 break; 340 default: 341 break; 342 } 343 } 344 345 var loff = (trans==1) ? this.padding.left + offx : this.padding.left; 346 var toff = (trans==1) ? this.padding.top + offy : this.padding.top; 347 var roff = (trans==-1) ? this.padding.right + offx : this.padding.right; 348 var boff = (trans==-1) ? this.padding.bottom + offy : this.padding.bottom; 349 350 var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; 351 var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; 352 var fill = (opts.fill != undefined) ? opts.fill : this.fill; 353 var cw = ctx.canvas.width; 354 var ch = ctx.canvas.height; 355 this._bases[0] = cw - loff - roff; 356 var ltot = this._length = ch - toff - boff; 357 358 var hend = this._bases[0]*this.widthRatio; 359 this._atot = ltot/2 * (this._bases[0] + this._bases[0]*this.widthRatio); 360 361 this._angle = Math.atan((this._bases[0] - hend)/2/ltot); 362 363 for (i=0; i<gd.length; i++) { 364 this._areas.push(gd[i][1] * this._atot); 365 } 366 367 368 var guess, err, count, lsum=0; 369 var tolerance = 0.0001; 370 371 for (i=0; i<this._areas.length; i++) { 372 guess = this._areas[i]/this._bases[i]; 373 err = 999999; 374 this._lengths[i] = guess; 375 count = 0; 376 while (err > this._lengths[i]*tolerance && count < 100) { 377 this._lengths[i] = this._areas[i]/(this._bases[i] - this._lengths[i] * Math.tan(this._angle)); 378 err = Math.abs(this._lengths[i] - guess); 379 this._bases[i+1] = this._bases[i] - (2*this._lengths[i]*Math.tan(this._angle)); 380 guess = this._lengths[i]; 381 count++; 382 } 383 lsum += this._lengths[i]; 384 } 385 386 // figure out vertices of each section 387 this._vertices = new Array(gd.length); 388 389 // these are 4 coners of entire trapezoid 390 var p0 = [loff, toff], 391 p1 = [loff+this._bases[0], toff], 392 p2 = [loff + (this._bases[0] - this._bases[this._bases.length-1])/2, toff + this._length], 393 p3 = [p2[0] + this._bases[this._bases.length-1], p2[1]]; 394 395 // equations of right and left sides, returns x, y values given height of section (y value) 396 function findleft (l) { 397 var m = (p0[1] - p2[1])/(p0[0] - p2[0]); 398 var b = p0[1] - m*p0[0]; 399 var y = l + p0[1]; 400 401 return [(y - b)/m, y]; 402 } 403 404 function findright (l) { 405 var m = (p1[1] - p3[1])/(p1[0] - p3[0]); 406 var b = p1[1] - m*p1[0]; 407 var y = l + p1[1]; 408 409 return [(y - b)/m, y]; 410 } 411 412 var x = offx, y = offy; 413 var h=0, adj=0; 414 415 for (i=0; i<gd.length; i++) { 416 this._vertices[i] = new Array(); 417 var v = this._vertices[i]; 418 var sm = this.sectionMargin; 419 if (i == 0) { 420 adj = 0; 421 } 422 if (i == 1) { 423 adj = sm/3; 424 } 425 else if (i > 0 && i < gd.length-1) { 426 adj = sm/2; 427 } 428 else if (i == gd.length -1) { 429 adj = 2*sm/3; 430 } 431 v.push(findleft(h+adj)); 432 v.push(findright(h+adj)); 433 h += this._lengths[i]; 434 if (i == 0) { 435 adj = -2*sm/3; 436 } 437 else if (i > 0 && i < gd.length-1) { 438 adj = -sm/2; 439 } 440 else if (i == gd.length - 1) { 441 adj = 0; 442 } 443 v.push(findright(h+adj)); 444 v.push(findleft(h+adj)); 445 446 } 447 448 if (this.shadow) { 449 var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; 450 for (var i=0; i<gd.length; i++) { 451 this.renderer.drawSection.call (this, ctx, this._vertices[i], shadowColor, true); 452 } 453 454 } 455 for (var i=0; i<gd.length; i++) { 456 var v = this._vertices[i]; 457 this.renderer.drawSection.call (this, ctx, v, this.seriesColors[i]); 458 459 if (this.showDataLabels && gd[i][1]*100 >= this.dataLabelThreshold) { 460 var fstr, label; 461 462 if (this.dataLabels == 'label') { 463 fstr = this.dataLabelFormatString || '%s'; 464 label = $.jqplot.sprintf(fstr, gd[i][0]); 465 } 466 else if (this.dataLabels == 'value') { 467 fstr = this.dataLabelFormatString || '%d'; 468 label = $.jqplot.sprintf(fstr, this.data[i][1]); 469 } 470 else if (this.dataLabels == 'percent') { 471 fstr = this.dataLabelFormatString || '%d%%'; 472 label = $.jqplot.sprintf(fstr, gd[i][1]*100); 473 } 474 else if (this.dataLabels.constructor == Array) { 475 fstr = this.dataLabelFormatString || '%s'; 476 label = $.jqplot.sprintf(fstr, this.dataLabels[this._dataIndices[i]]); 477 } 478 479 var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge; 480 481 var x = (v[0][0] + v[1][0])/2 + this.canvas._offsets.left; 482 var y = (v[1][1] + v[2][1])/2 + this.canvas._offsets.top; 483 484 var labelelem = $('<span class="jqplot-funnel-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem); 485 x -= labelelem.width()/2; 486 y -= labelelem.height()/2; 487 x = Math.round(x); 488 y = Math.round(y); 489 labelelem.css({left: x, top: y}); 490 } 491 492 } 493 494 }; 495 496 $.jqplot.FunnelAxisRenderer = function() { 497 $.jqplot.LinearAxisRenderer.call(this); 498 }; 499 500 $.jqplot.FunnelAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); 501 $.jqplot.FunnelAxisRenderer.prototype.constructor = $.jqplot.FunnelAxisRenderer; 502 503 504 // There are no traditional axes on a funnel chart. We just need to provide 505 // dummy objects with properties so the plot will render. 506 // called with scope of axis object. 507 $.jqplot.FunnelAxisRenderer.prototype.init = function(options){ 508 // 509 this.tickRenderer = $.jqplot.FunnelTickRenderer; 510 $.extend(true, this, options); 511 // I don't think I'm going to need _dataBounds here. 512 // have to go Axis scaling in a way to fit chart onto plot area 513 // and provide u2p and p2u functionality for mouse cursor, etc. 514 // for convienence set _dataBounds to 0 and 100 and 515 // set min/max to 0 and 100. 516 this._dataBounds = {min:0, max:100}; 517 this.min = 0; 518 this.max = 100; 519 this.showTicks = false; 520 this.ticks = []; 521 this.showMark = false; 522 this.show = false; 523 }; 524 525 526 527 /** 528 * Class: $.jqplot.FunnelLegendRenderer 529 * Legend Renderer specific to funnel plots. Set by default 530 * when the user creates a funnel plot. 531 */ 532 $.jqplot.FunnelLegendRenderer = function(){ 533 $.jqplot.TableLegendRenderer.call(this); 534 }; 535 536 $.jqplot.FunnelLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); 537 $.jqplot.FunnelLegendRenderer.prototype.constructor = $.jqplot.FunnelLegendRenderer; 538 539 $.jqplot.FunnelLegendRenderer.prototype.init = function(options) { 540 // Group: Properties 541 // 542 // prop: numberRows 543 // Maximum number of rows in the legend. 0 or null for unlimited. 544 this.numberRows = null; 545 // prop: numberColumns 546 // Maximum number of columns in the legend. 0 or null for unlimited. 547 this.numberColumns = null; 548 $.extend(true, this, options); 549 }; 550 551 // called with context of legend 552 $.jqplot.FunnelLegendRenderer.prototype.draw = function() { 553 var legend = this; 554 if (this.show) { 555 var series = this._series; 556 var ss = 'position:absolute;'; 557 ss += (this.background) ? 'background:'+this.background+';' : ''; 558 ss += (this.border) ? 'border:'+this.border+';' : ''; 559 ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; 560 ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; 561 ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; 562 ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; 563 ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; 564 ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; 565 ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; 566 this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); 567 // Funnel charts legends don't go by number of series, but by number of data points 568 // in the series. Refactor things here for that. 569 570 var pad = false, 571 reverse = false, 572 nr, nc; 573 var s = series[0]; 574 var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); 575 576 if (s.show) { 577 var pd = s.data; 578 if (this.numberRows) { 579 nr = this.numberRows; 580 if (!this.numberColumns){ 581 nc = Math.ceil(pd.length/nr); 582 } 583 else{ 584 nc = this.numberColumns; 585 } 586 } 587 else if (this.numberColumns) { 588 nc = this.numberColumns; 589 nr = Math.ceil(pd.length/this.numberColumns); 590 } 591 else { 592 nr = pd.length; 593 nc = 1; 594 } 595 596 var i, j, tr, td1, td2, lt, rs, color; 597 var idx = 0; 598 599 for (i=0; i<nr; i++) { 600 if (reverse){ 601 tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem); 602 } 603 else{ 604 tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem); 605 } 606 for (j=0; j<nc; j++) { 607 if (idx < pd.length){ 608 lt = this.labels[idx] || pd[idx][0].toString(); 609 color = colorGenerator.next(); 610 if (!reverse){ 611 if (i>0){ 612 pad = true; 613 } 614 else{ 615 pad = false; 616 } 617 } 618 else{ 619 if (i == nr -1){ 620 pad = false; 621 } 622 else{ 623 pad = true; 624 } 625 } 626 rs = (pad) ? this.rowSpacing : '0'; 627 628 td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ 629 '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+ 630 '</div></td>'); 631 td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); 632 if (this.escapeHtml){ 633 td2.text(lt); 634 } 635 else { 636 td2.html(lt); 637 } 638 if (reverse) { 639 td2.prependTo(tr); 640 td1.prependTo(tr); 641 } 642 else { 643 td1.appendTo(tr); 644 td2.appendTo(tr); 645 } 646 pad = true; 647 } 648 idx++; 649 } 650 } 651 } 652 } 653 return this._elem; 654 }; 655 656 // $.jqplot.FunnelLegendRenderer.prototype.pack = function(offsets) { 657 // if (this.show) { 658 // // fake a grid for positioning 659 // var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom}; 660 // if (this.placement == 'insideGrid') { 661 // switch (this.location) { 662 // case 'nw': 663 // var a = grid._left + this.xoffset; 664 // var b = grid._top + this.yoffset; 665 // this._elem.css('left', a); 666 // this._elem.css('top', b); 667 // break; 668 // case 'n': 669 // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 670 // var b = grid._top + this.yoffset; 671 // this._elem.css('left', a); 672 // this._elem.css('top', b); 673 // break; 674 // case 'ne': 675 // var a = offsets.right + this.xoffset; 676 // var b = grid._top + this.yoffset; 677 // this._elem.css({right:a, top:b}); 678 // break; 679 // case 'e': 680 // var a = offsets.right + this.xoffset; 681 // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 682 // this._elem.css({right:a, top:b}); 683 // break; 684 // case 'se': 685 // var a = offsets.right + this.xoffset; 686 // var b = offsets.bottom + this.yoffset; 687 // this._elem.css({right:a, bottom:b}); 688 // break; 689 // case 's': 690 // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 691 // var b = offsets.bottom + this.yoffset; 692 // this._elem.css({left:a, bottom:b}); 693 // break; 694 // case 'sw': 695 // var a = grid._left + this.xoffset; 696 // var b = offsets.bottom + this.yoffset; 697 // this._elem.css({left:a, bottom:b}); 698 // break; 699 // case 'w': 700 // var a = grid._left + this.xoffset; 701 // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 702 // this._elem.css({left:a, top:b}); 703 // break; 704 // default: // same as 'se' 705 // var a = grid._right - this.xoffset; 706 // var b = grid._bottom + this.yoffset; 707 // this._elem.css({right:a, bottom:b}); 708 // break; 709 // } 710 // 711 // } 712 // else { 713 // switch (this.location) { 714 // case 'nw': 715 // var a = this._plotDimensions.width - grid._left + this.xoffset; 716 // var b = grid._top + this.yoffset; 717 // this._elem.css('right', a); 718 // this._elem.css('top', b); 719 // break; 720 // case 'n': 721 // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 722 // var b = this._plotDimensions.height - grid._top + this.yoffset; 723 // this._elem.css('left', a); 724 // this._elem.css('bottom', b); 725 // break; 726 // case 'ne': 727 // var a = this._plotDimensions.width - offsets.right + this.xoffset; 728 // var b = grid._top + this.yoffset; 729 // this._elem.css({left:a, top:b}); 730 // break; 731 // case 'e': 732 // var a = this._plotDimensions.width - offsets.right + this.xoffset; 733 // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 734 // this._elem.css({left:a, top:b}); 735 // break; 736 // case 'se': 737 // var a = this._plotDimensions.width - offsets.right + this.xoffset; 738 // var b = offsets.bottom + this.yoffset; 739 // this._elem.css({left:a, bottom:b}); 740 // break; 741 // case 's': 742 // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; 743 // var b = this._plotDimensions.height - offsets.bottom + this.yoffset; 744 // this._elem.css({left:a, top:b}); 745 // break; 746 // case 'sw': 747 // var a = this._plotDimensions.width - grid._left + this.xoffset; 748 // var b = offsets.bottom + this.yoffset; 749 // this._elem.css({right:a, bottom:b}); 750 // break; 751 // case 'w': 752 // var a = this._plotDimensions.width - grid._left + this.xoffset; 753 // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; 754 // this._elem.css({right:a, top:b}); 755 // break; 756 // default: // same as 'se' 757 // var a = grid._right - this.xoffset; 758 // var b = grid._bottom + this.yoffset; 759 // this._elem.css({right:a, bottom:b}); 760 // break; 761 // } 762 // } 763 // } 764 // }; 765 766 // setup default renderers for axes and legend so user doesn't have to 767 // called with scope of plot 768 function preInit(target, data, options) { 769 options = options || {}; 770 options.axesDefaults = options.axesDefaults || {}; 771 options.legend = options.legend || {}; 772 options.seriesDefaults = options.seriesDefaults || {}; 773 // only set these if there is a funnel series 774 var setopts = false; 775 if (options.seriesDefaults.renderer == $.jqplot.FunnelRenderer) { 776 setopts = true; 777 } 778 else if (options.series) { 779 for (var i=0; i < options.series.length; i++) { 780 if (options.series[i].renderer == $.jqplot.FunnelRenderer) { 781 setopts = true; 782 } 783 } 784 } 785 786 if (setopts) { 787 options.axesDefaults.renderer = $.jqplot.FunnelAxisRenderer; 788 options.legend.renderer = $.jqplot.FunnelLegendRenderer; 789 options.legend.preDraw = true; 790 options.sortData = false; 791 options.seriesDefaults.pointLabels = {show: false}; 792 } 793 } 794 795 function postInit(target, data, options) { 796 // if multiple series, add a reference to the previous one so that 797 // funnel rings can nest. 798 for (var i=0; i<this.series.length; i++) { 799 if (this.series[i].renderer.constructor == $.jqplot.FunnelRenderer) { 800 // don't allow mouseover and mousedown at same time. 801 if (this.series[i].highlightMouseOver) { 802 this.series[i].highlightMouseDown = false; 803 } 804 } 805 } 806 } 807 808 // called with scope of plot 809 function postParseOptions(options) { 810 for (var i=0; i<this.series.length; i++) { 811 this.series[i].seriesColors = this.seriesColors; 812 this.series[i].colorGenerator = $.jqplot.colorGenerator; 813 } 814 } 815 816 function highlight (plot, sidx, pidx) { 817 var s = plot.series[sidx]; 818 var canvas = plot.plugins.funnelRenderer.highlightCanvas; 819 canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); 820 s._highlightedPoint = pidx; 821 plot.plugins.funnelRenderer.highlightedSeriesIndex = sidx; 822 s.renderer.drawSection.call(s, canvas._ctx, s._vertices[pidx], s.highlightColors[pidx], false); 823 } 824 825 function unhighlight (plot) { 826 var canvas = plot.plugins.funnelRenderer.highlightCanvas; 827 canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); 828 for (var i=0; i<plot.series.length; i++) { 829 plot.series[i]._highlightedPoint = null; 830 } 831 plot.plugins.funnelRenderer.highlightedSeriesIndex = null; 832 plot.target.trigger('jqplotDataUnhighlight'); 833 } 834 835 function handleMove(ev, gridpos, datapos, neighbor, plot) { 836 if (neighbor) { 837 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 838 var evt1 = jQuery.Event('jqplotDataMouseOver'); 839 evt1.pageX = ev.pageX; 840 evt1.pageY = ev.pageY; 841 plot.target.trigger(evt1, ins); 842 if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { 843 var evt = jQuery.Event('jqplotDataHighlight'); 844 evt.which = ev.which; 845 evt.pageX = ev.pageX; 846 evt.pageY = ev.pageY; 847 plot.target.trigger(evt, ins); 848 highlight (plot, ins[0], ins[1]); 849 } 850 } 851 else if (neighbor == null) { 852 unhighlight (plot); 853 } 854 } 855 856 function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { 857 if (neighbor) { 858 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 859 if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { 860 var evt = jQuery.Event('jqplotDataHighlight'); 861 evt.which = ev.which; 862 evt.pageX = ev.pageX; 863 evt.pageY = ev.pageY; 864 plot.target.trigger(evt, ins); 865 highlight (plot, ins[0], ins[1]); 866 } 867 } 868 else if (neighbor == null) { 869 unhighlight (plot); 870 } 871 } 872 873 function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { 874 var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex; 875 if (idx != null && plot.series[idx].highlightMouseDown) { 876 unhighlight(plot); 877 } 878 } 879 880 function handleClick(ev, gridpos, datapos, neighbor, plot) { 881 if (neighbor) { 882 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 883 var evt = jQuery.Event('jqplotDataClick'); 884 evt.which = ev.which; 885 evt.pageX = ev.pageX; 886 evt.pageY = ev.pageY; 887 plot.target.trigger(evt, ins); 888 } 889 } 890 891 function handleRightClick(ev, gridpos, datapos, neighbor, plot) { 892 if (neighbor) { 893 var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; 894 var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex; 895 if (idx != null && plot.series[idx].highlightMouseDown) { 896 unhighlight(plot); 897 } 898 var evt = jQuery.Event('jqplotDataRightClick'); 899 evt.which = ev.which; 900 evt.pageX = ev.pageX; 901 evt.pageY = ev.pageY; 902 plot.target.trigger(evt, ins); 903 } 904 } 905 906 // called within context of plot 907 // create a canvas which we can draw on. 908 // insert it before the eventCanvas, so eventCanvas will still capture events. 909 function postPlotDraw() { 910 // Memory Leaks patch 911 if (this.plugins.funnelRenderer && this.plugins.funnelRenderer.highlightCanvas) { 912 this.plugins.funnelRenderer.highlightCanvas.resetCanvas(); 913 this.plugins.funnelRenderer.highlightCanvas = null; 914 } 915 916 this.plugins.funnelRenderer = {}; 917 this.plugins.funnelRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); 918 919 // do we have any data labels? if so, put highlight canvas before those 920 var labels = $(this.targetId+' .jqplot-data-label'); 921 if (labels.length) { 922 $(labels[0]).before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this)); 923 } 924 // else put highlight canvas before event canvas. 925 else { 926 this.eventCanvas._elem.before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this)); 927 } 928 var hctx = this.plugins.funnelRenderer.highlightCanvas.setContext(); 929 this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); 930 } 931 932 $.jqplot.preInitHooks.push(preInit); 933 934 $.jqplot.FunnelTickRenderer = function() { 935 $.jqplot.AxisTickRenderer.call(this); 936 }; 937 938 $.jqplot.FunnelTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); 939 $.jqplot.FunnelTickRenderer.prototype.constructor = $.jqplot.FunnelTickRenderer; 940 941 })(jQuery); 942 943
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:08:37 2014 | Cross-referenced by PHPXref 0.7.1 |