You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

754 lines
30 KiB

  1. /**
  2. * jqPlot
  3. * Pure JavaScript plotting plugin using jQuery
  4. *
  5. * Version: 1.0.0b2_r792
  6. *
  7. * Copyright (c) 2009-2011 Chris Leonello
  8. * jqPlot is currently available for use in all personal or commercial projects
  9. * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
  10. * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
  11. * choose the license that best suits your project and use it accordingly.
  12. *
  13. * Although not required, the author would appreciate an email letting him
  14. * know of any substantial use of jqPlot. You can reach the author at:
  15. * chris at jqplot dot com or see http://www.jqplot.com/info.php .
  16. *
  17. * If you are feeling kind and generous, consider supporting the project by
  18. * making a donation at: http://www.jqplot.com/donate.php .
  19. *
  20. * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
  21. *
  22. * version 2007.04.27
  23. * author Ash Searle
  24. * http://hexmen.com/blog/2007/03/printf-sprintf/
  25. * http://hexmen.com/js/sprintf.js
  26. * The author (Ash Searle) has placed this code in the public domain:
  27. * "This code is unrestricted: you are free to use it however you like."
  28. *
  29. */
  30. (function($) {
  31. var arrayMax = function( array ){
  32. return Math.max.apply( Math, array );
  33. };
  34. var arrayMin = function( array ){
  35. return Math.min.apply( Math, array );
  36. };
  37. /**
  38. * Class: $.jqplot.BubbleRenderer
  39. * Plugin renderer to draw a bubble chart. A Bubble chart has data points displayed as
  40. * colored circles with an optional text label inside. To use
  41. * the bubble renderer, you must include the bubble renderer like:
  42. *
  43. * > <script language="javascript" type="text/javascript" src="../src/plugins/jqplot.bubbleRenderer.js"></script>
  44. *
  45. * Data must be supplied in
  46. * the form:
  47. *
  48. * > [[x1, y1, r1, <label or {label:'text', color:color}>], ...]
  49. *
  50. * where the label or options
  51. * object is optional.
  52. *
  53. * Note that all bubble colors will be the same
  54. * unless the "varyBubbleColors" option is set to true. Colors can be specified in the data array
  55. * or in the seriesColors array option on the series. If no colors are defined, the default jqPlot
  56. * series of 16 colors are used. Colors are automatically cycled around again if there are more
  57. * bubbles than colors.
  58. *
  59. * Bubbles are autoscaled by default to fit within the chart area while maintaining
  60. * relative sizes. If the "autoscaleBubbles" option is set to false, the r(adius) values
  61. * in the data array a treated as literal pixel values for the radii of the bubbles.
  62. *
  63. * Properties are passed into the bubble renderer in the rendererOptions object of
  64. * the series options like:
  65. *
  66. * > seriesDefaults: {
  67. * > renderer: $.jqplot.BubbleRenderer,
  68. * > rendererOptions: {
  69. * > bubbleAlpha: 0.7,
  70. * > varyBubbleColors: false
  71. * > }
  72. * > }
  73. *
  74. */
  75. $.jqplot.BubbleRenderer = function(){
  76. $.jqplot.LineRenderer.call(this);
  77. };
  78. $.jqplot.BubbleRenderer.prototype = new $.jqplot.LineRenderer();
  79. $.jqplot.BubbleRenderer.prototype.constructor = $.jqplot.BubbleRenderer;
  80. // called with scope of a series
  81. $.jqplot.BubbleRenderer.prototype.init = function(options, plot) {
  82. // Group: Properties
  83. //
  84. // prop: varyBubbleColors
  85. // True to vary the color of each bubble in this series according to
  86. // the seriesColors array. False to set each bubble to the color
  87. // specified on this series. This has no effect if a css background color
  88. // option is specified in the renderer css options.
  89. this.varyBubbleColors = true;
  90. // prop: autoscaleBubbles
  91. // True to scale the bubble radius based on plot size.
  92. // False will use the radius value as provided as a raw pixel value for
  93. // bubble radius.
  94. this.autoscaleBubbles = true;
  95. // prop: autoscaleMultiplier
  96. // Multiplier the bubble size if autoscaleBubbles is true.
  97. this.autoscaleMultiplier = 1.0;
  98. // prop: autoscalePointsFactor
  99. // Factor which decreases bubble size based on how many bubbles on on the chart.
  100. // 0 means no adjustment for number of bubbles. Negative values will decrease
  101. // size of bubbles as more bubbles are added. Values between 0 and -0.2
  102. // should work well.
  103. this.autoscalePointsFactor = -0.07;
  104. // prop: escapeHtml
  105. // True to escape html in bubble label text.
  106. this.escapeHtml = true;
  107. // prop: highlightMouseOver
  108. // True to highlight bubbles when moused over.
  109. // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
  110. this.highlightMouseOver = true;
  111. // prop: highlightMouseDown
  112. // True to highlight when a mouse button is pressed over a bubble.
  113. // This will be disabled if highlightMouseOver is true.
  114. this.highlightMouseDown = false;
  115. // prop: highlightColors
  116. // An array of colors to use when highlighting a slice. Calculated automatically
  117. // if not supplied.
  118. this.highlightColors = [];
  119. // prop: bubbleAlpha
  120. // Alpha transparency to apply to all bubbles in this series.
  121. this.bubbleAlpha = 1.0;
  122. // prop: highlightAlpha
  123. // Alpha transparency to apply when highlighting bubble.
  124. // Set to value of bubbleAlpha by default.
  125. this.highlightAlpha = null;
  126. // prop: bubbleGradients
  127. // True to color the bubbles with gradient fills instead of flat colors.
  128. // NOT AVAILABLE IN IE due to lack of excanvas support for radial gradient fills.
  129. // will be ignored in IE.
  130. this.bubbleGradients = false;
  131. // prop: showLabels
  132. // True to show labels on bubbles (if any), false to not show.
  133. this.showLabels = true;
  134. // array of [point index, radius] which will be sorted in descending order to plot
  135. // largest points below smaller points.
  136. this.radii = [];
  137. this.maxRadius = 0;
  138. // index of the currenty highlighted point, if any
  139. this._highlightedPoint = null;
  140. // array of jQuery labels.
  141. this.labels = [];
  142. this.bubbleCanvases = [];
  143. this._type = 'bubble';
  144. // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
  145. if (options.highlightMouseDown && options.highlightMouseOver == null) {
  146. options.highlightMouseOver = false;
  147. }
  148. $.extend(true, this, options);
  149. if (this.highlightAlpha == null) {
  150. this.highlightAlpha = this.bubbleAlpha;
  151. if (this.bubbleGradients) {
  152. this.highlightAlpha = 0.35;
  153. }
  154. }
  155. this.autoscaleMultiplier = this.autoscaleMultiplier * Math.pow(this.data.length, this.autoscalePointsFactor);
  156. // index of the currenty highlighted point, if any
  157. this._highlightedPoint = null;
  158. // adjust the series colors for options colors passed in with data or for alpha.
  159. // note, this can leave undefined holes in the seriesColors array.
  160. var comps;
  161. for (var i=0; i<this.data.length; i++) {
  162. var color = null;
  163. var d = this.data[i];
  164. this.maxRadius = Math.max(this.maxRadius, d[2]);
  165. if (d[3]) {
  166. if (typeof(d[3]) == 'object') {
  167. color = d[3]['color'];
  168. }
  169. }
  170. if (color == null) {
  171. if (this.seriesColors[i] != null) {
  172. color = this.seriesColors[i];
  173. }
  174. }
  175. if (color && this.bubbleAlpha < 1.0) {
  176. comps = $.jqplot.getColorComponents(color);
  177. color = 'rgba('+comps[0]+', '+comps[1]+', '+comps[2]+', '+this.bubbleAlpha+')';
  178. }
  179. if (color) {
  180. this.seriesColors[i] = color;
  181. }
  182. }
  183. if (!this.varyBubbleColors) {
  184. this.seriesColors = [this.color];
  185. }
  186. this.colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
  187. // set highlight colors if none provided
  188. if (this.highlightColors.length == 0) {
  189. for (var i=0; i<this.seriesColors.length; i++){
  190. var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
  191. var newrgb = [rgba[0], rgba[1], rgba[2]];
  192. var sum = newrgb[0] + newrgb[1] + newrgb[2];
  193. for (var j=0; j<3; j++) {
  194. // when darkening, lowest color component can be is 60.
  195. newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
  196. newrgb[j] = parseInt(newrgb[j], 10);
  197. }
  198. this.highlightColors.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+', '+this.highlightAlpha+')');
  199. }
  200. }
  201. this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors);
  202. var sopts = {fill:true, isarc:true, angle:this.shadowAngle, alpha:this.shadowAlpha, closePath:true};
  203. this.renderer.shadowRenderer.init(sopts);
  204. this.canvas = new $.jqplot.DivCanvas();
  205. this.canvas._plotDimensions = this._plotDimensions;
  206. plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
  207. plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
  208. plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
  209. plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
  210. plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
  211. plot.postDrawHooks.addOnce(postPlotDraw);
  212. };
  213. // converts the user data values to grid coordinates and stores them
  214. // in the gridData array.
  215. // Called with scope of a series.
  216. $.jqplot.BubbleRenderer.prototype.setGridData = function(plot) {
  217. // recalculate the grid data
  218. var xp = this._xaxis.series_u2p;
  219. var yp = this._yaxis.series_u2p;
  220. var data = this._plotData;
  221. this.gridData = [];
  222. var radii = [];
  223. this.radii = [];
  224. var dim = Math.min(plot._height, plot._width);
  225. for (var i=0; i<this.data.length; i++) {
  226. if (data[i] != null) {
  227. this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1]), data[i][2]]);
  228. this.radii.push([i, data[i][2]]);
  229. radii.push(data[i][2]);
  230. }
  231. }
  232. var r, val, maxr = this.maxRadius = arrayMax(radii);
  233. var l = this.gridData.length;
  234. if (this.autoscaleBubbles) {
  235. for (var i=0; i<l; i++) {
  236. val = radii[i]/maxr;
  237. r = this.autoscaleMultiplier * dim / 6;
  238. this.gridData[i][2] = r * val;
  239. }
  240. }
  241. this.radii.sort(function(a, b) { return b[1] - a[1]; });
  242. };
  243. // converts any arbitrary data values to grid coordinates and
  244. // returns them. This method exists so that plugins can use a series'
  245. // linerenderer to generate grid data points without overwriting the
  246. // grid data associated with that series.
  247. // Called with scope of a series.
  248. $.jqplot.BubbleRenderer.prototype.makeGridData = function(data, plot) {
  249. // recalculate the grid data
  250. var xp = this._xaxis.series_u2p;
  251. var yp = this._yaxis.series_u2p;
  252. var gd = [];
  253. var radii = [];
  254. this.radii = [];
  255. var dim = Math.min(plot._height, plot._width);
  256. for (var i=0; i<data.length; i++) {
  257. if (data[i] != null) {
  258. gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1]), data[i][2]]);
  259. radii.push(data[i][2]);
  260. this.radii.push([i, data[i][2]]);
  261. }
  262. }
  263. var r, val, maxr = this.maxRadius = arrayMax(radii);
  264. var l = this.gridData.length;
  265. if (this.autoscaleBubbles) {
  266. for (var i=0; i<l; i++) {
  267. val = radii[i]/maxr;
  268. r = this.autoscaleMultiplier * dim / 6;
  269. gd[i][2] = r * val;
  270. }
  271. }
  272. this.radii.sort(function(a, b) { return b[1] - a[1]; });
  273. return gd;
  274. };
  275. // called with scope of series
  276. $.jqplot.BubbleRenderer.prototype.draw = function (ctx, gd, options) {
  277. if (this.plugins.pointLabels) {
  278. this.plugins.pointLabels.show = false;
  279. }
  280. var opts = (options != undefined) ? options : {};
  281. var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
  282. this.canvas._elem.empty();
  283. for (var i=0; i<this.radii.length; i++) {
  284. var idx = this.radii[i][0];
  285. var t=null;
  286. var color = null;
  287. var el = null;
  288. var tel = null;
  289. var d = this.data[idx];
  290. var gd = this.gridData[idx];
  291. if (d[3]) {
  292. if (typeof(d[3]) == 'object') {
  293. t = d[3]['label'];
  294. }
  295. else if (typeof(d[3]) == 'string') {
  296. t = d[3];
  297. }
  298. }
  299. // color = (this.varyBubbleColors) ? this.colorGenerator.get(idx) : this.color;
  300. color = this.colorGenerator.get(idx);
  301. // If we're drawing a shadow, expand the canvas dimensions to accomodate.
  302. var canvasRadius = gd[2];
  303. var offset, depth;
  304. if (this.shadow) {
  305. offset = (0.7 + gd[2]/40).toFixed(1);
  306. depth = 1 + Math.ceil(gd[2]/15);
  307. canvasRadius += offset*depth;
  308. }
  309. this.bubbleCanvases[idx] = new $.jqplot.BubbleCanvas();
  310. this.canvas._elem.append(this.bubbleCanvases[idx].createElement(gd[0], gd[1], canvasRadius));
  311. this.bubbleCanvases[idx].setContext();
  312. var ctx = this.bubbleCanvases[idx]._ctx;
  313. var x = ctx.canvas.width/2;
  314. var y = ctx.canvas.height/2;
  315. if (this.shadow) {
  316. this.renderer.shadowRenderer.draw(ctx, [x, y, gd[2], 0, 2*Math.PI], {offset: offset, depth: depth});
  317. }
  318. this.bubbleCanvases[idx].draw(gd[2], color, this.bubbleGradients, this.shadowAngle/180*Math.PI);
  319. // now draw label.
  320. if (t && this.showLabels) {
  321. tel = $('<div style="position:absolute;" class="jqplot-bubble-label"></div>');
  322. if (this.escapeHtml) {
  323. tel.text(t);
  324. }
  325. else {
  326. tel.html(t);
  327. }
  328. this.canvas._elem.append(tel);
  329. var h = $(tel).outerHeight();
  330. var w = $(tel).outerWidth();
  331. var top = gd[1] - 0.5*h;
  332. var left = gd[0] - 0.5*w;
  333. tel.css({top: top, left: left});
  334. this.labels[idx] = $(tel);
  335. }
  336. }
  337. };
  338. $.jqplot.DivCanvas = function() {
  339. $.jqplot.ElemContainer.call(this);
  340. this._ctx;
  341. };
  342. $.jqplot.DivCanvas.prototype = new $.jqplot.ElemContainer();
  343. $.jqplot.DivCanvas.prototype.constructor = $.jqplot.DivCanvas;
  344. $.jqplot.DivCanvas.prototype.createElement = function(offsets, clss, plotDimensions) {
  345. this._offsets = offsets;
  346. var klass = 'jqplot-DivCanvas';
  347. if (clss != undefined) {
  348. klass = clss;
  349. }
  350. var elem;
  351. // if this canvas already has a dom element, don't make a new one.
  352. if (this._elem) {
  353. elem = this._elem.get(0);
  354. }
  355. else {
  356. elem = document.createElement('div');
  357. }
  358. // if new plotDimensions supplied, use them.
  359. if (plotDimensions != undefined) {
  360. this._plotDimensions = plotDimensions;
  361. }
  362. var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px';
  363. var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px';
  364. this._elem = $(elem);
  365. this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top });
  366. this._elem.addClass(klass);
  367. return this._elem;
  368. };
  369. $.jqplot.DivCanvas.prototype.setContext = function() {
  370. this._ctx = {
  371. canvas:{
  372. width:0,
  373. height:0
  374. },
  375. clearRect:function(){return null;}
  376. };
  377. return this._ctx;
  378. };
  379. $.jqplot.BubbleCanvas = function() {
  380. $.jqplot.ElemContainer.call(this);
  381. this._ctx;
  382. };
  383. $.jqplot.BubbleCanvas.prototype = new $.jqplot.ElemContainer();
  384. $.jqplot.BubbleCanvas.prototype.constructor = $.jqplot.BubbleCanvas;
  385. // initialize with the x,y pont of bubble center and the bubble radius.
  386. $.jqplot.BubbleCanvas.prototype.createElement = function(x, y, r) {
  387. var klass = 'jqplot-bubble-point';
  388. var elem;
  389. // if this canvas already has a dom element, don't make a new one.
  390. if (this._elem) {
  391. elem = this._elem.get(0);
  392. }
  393. else {
  394. elem = document.createElement('canvas');
  395. }
  396. elem.width = (r != null) ? 2*r : elem.width;
  397. elem.height = (r != null) ? 2*r : elem.height;
  398. this._elem = $(elem);
  399. var l = (x != null && r != null) ? x - r : this._elem.css('left');
  400. var t = (y != null && r != null) ? y - r : this._elem.css('top');
  401. this._elem.css({ position: 'absolute', left: l, top: t });
  402. this._elem.addClass(klass);
  403. if ($.jqplot.use_excanvas) {
  404. window.G_vmlCanvasManager.init_(document);
  405. elem = window.G_vmlCanvasManager.initElement(elem);
  406. }
  407. return this._elem;
  408. };
  409. $.jqplot.BubbleCanvas.prototype.draw = function(r, color, gradients, angle) {
  410. var ctx = this._ctx;
  411. // r = Math.floor(r*1.04);
  412. // var x = Math.round(ctx.canvas.width/2);
  413. // var y = Math.round(ctx.canvas.height/2);
  414. var x = ctx.canvas.width/2;
  415. var y = ctx.canvas.height/2;
  416. ctx.save();
  417. if (gradients && !$.jqplot.use_excanvas) {
  418. r = r*1.04;
  419. var comps = $.jqplot.getColorComponents(color);
  420. var colorinner = 'rgba('+Math.round(comps[0]+0.8*(255-comps[0]))+', '+Math.round(comps[1]+0.8*(255-comps[1]))+', '+Math.round(comps[2]+0.8*(255-comps[2]))+', '+comps[3]+')';
  421. var colorend = 'rgba('+comps[0]+', '+comps[1]+', '+comps[2]+', 0)';
  422. // var rinner = Math.round(0.35 * r);
  423. // var xinner = Math.round(x - Math.cos(angle) * 0.33 * r);
  424. // var yinner = Math.round(y - Math.sin(angle) * 0.33 * r);
  425. var rinner = 0.35 * r;
  426. var xinner = x - Math.cos(angle) * 0.33 * r;
  427. var yinner = y - Math.sin(angle) * 0.33 * r;
  428. var radgrad = ctx.createRadialGradient(xinner, yinner, rinner, x, y, r);
  429. radgrad.addColorStop(0, colorinner);
  430. radgrad.addColorStop(0.93, color);
  431. radgrad.addColorStop(0.96, colorend);
  432. radgrad.addColorStop(1, colorend);
  433. // radgrad.addColorStop(.98, colorend);
  434. ctx.fillStyle = radgrad;
  435. ctx.fillRect(0,0, ctx.canvas.width, ctx.canvas.height);
  436. }
  437. else {
  438. ctx.fillStyle = color;
  439. ctx.strokeStyle = color;
  440. ctx.lineWidth = 1;
  441. ctx.beginPath();
  442. var ang = 2*Math.PI;
  443. ctx.arc(x, y, r, 0, ang, 0);
  444. ctx.closePath();
  445. ctx.fill();
  446. }
  447. ctx.restore();
  448. };
  449. $.jqplot.BubbleCanvas.prototype.setContext = function() {
  450. this._ctx = this._elem.get(0).getContext("2d");
  451. return this._ctx;
  452. };
  453. $.jqplot.BubbleAxisRenderer = function() {
  454. $.jqplot.LinearAxisRenderer.call(this);
  455. };
  456. $.jqplot.BubbleAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
  457. $.jqplot.BubbleAxisRenderer.prototype.constructor = $.jqplot.BubbleAxisRenderer;
  458. // called with scope of axis object.
  459. $.jqplot.BubbleAxisRenderer.prototype.init = function(options){
  460. $.extend(true, this, options);
  461. var db = this._dataBounds;
  462. var minsidx = 0,
  463. minpidx = 0,
  464. maxsidx = 0,
  465. maxpidx = 0,
  466. maxr = 0,
  467. minr = 0,
  468. minMaxRadius = 0,
  469. maxMaxRadius = 0,
  470. maxMult = 0,
  471. minMult = 0;
  472. // Go through all the series attached to this axis and find
  473. // the min/max bounds for this axis.
  474. for (var i=0; i<this._series.length; i++) {
  475. var s = this._series[i];
  476. var d = s._plotData;
  477. for (var j=0; j<d.length; j++) {
  478. if (this.name == 'xaxis' || this.name == 'x2axis') {
  479. if (d[j][0] < db.min || db.min == null) {
  480. db.min = d[j][0];
  481. minsidx=i;
  482. minpidx=j;
  483. minr = d[j][2];
  484. minMaxRadius = s.maxRadius;
  485. minMult = s.autoscaleMultiplier;
  486. }
  487. if (d[j][0] > db.max || db.max == null) {
  488. db.max = d[j][0];
  489. maxsidx=i;
  490. maxpidx=j;
  491. maxr = d[j][2];
  492. maxMaxRadius = s.maxRadius;
  493. maxMult = s.autoscaleMultiplier;
  494. }
  495. }
  496. else {
  497. if (d[j][1] < db.min || db.min == null) {
  498. db.min = d[j][1];
  499. minsidx=i;
  500. minpidx=j;
  501. minr = d[j][2];
  502. minMaxRadius = s.maxRadius;
  503. minMult = s.autoscaleMultiplier;
  504. }
  505. if (d[j][1] > db.max || db.max == null) {
  506. db.max = d[j][1];
  507. maxsidx=i;
  508. maxpidx=j;
  509. maxr = d[j][2];
  510. maxMaxRadius = s.maxRadius;
  511. maxMult = s.autoscaleMultiplier;
  512. }
  513. }
  514. }
  515. }
  516. var minRatio = minr/minMaxRadius;
  517. var maxRatio = maxr/maxMaxRadius;
  518. // need to estimate the effect of the radius on total axis span and adjust axis accordingly.
  519. var span = db.max - db.min;
  520. // var dim = (this.name == 'xaxis' || this.name == 'x2axis') ? this._plotDimensions.width : this._plotDimensions.height;
  521. var dim = Math.min(this._plotDimensions.width, this._plotDimensions.height);
  522. var minfact = minRatio * minMult/3 * span;
  523. var maxfact = maxRatio * maxMult/3 * span;
  524. db.max += maxfact;
  525. db.min -= minfact;
  526. };
  527. function highlight (plot, sidx, pidx) {
  528. plot.plugins.bubbleRenderer.highlightLabelCanvas.empty();
  529. var s = plot.series[sidx];
  530. var canvas = plot.plugins.bubbleRenderer.highlightCanvas;
  531. var ctx = canvas._ctx;
  532. ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
  533. s._highlightedPoint = pidx;
  534. plot.plugins.bubbleRenderer.highlightedSeriesIndex = sidx;
  535. var color = s.highlightColorGenerator.get(pidx);
  536. var x = s.gridData[pidx][0],
  537. y = s.gridData[pidx][1],
  538. r = s.gridData[pidx][2];
  539. ctx.save();
  540. ctx.fillStyle = color;
  541. ctx.strokeStyle = color;
  542. ctx.lineWidth = 1;
  543. ctx.beginPath();
  544. ctx.arc(x, y, r, 0, 2*Math.PI, 0);
  545. ctx.closePath();
  546. ctx.fill();
  547. ctx.restore();
  548. // bring label to front
  549. if (s.labels[pidx]) {
  550. plot.plugins.bubbleRenderer.highlightLabel = s.labels[pidx].clone();
  551. plot.plugins.bubbleRenderer.highlightLabel.appendTo(plot.plugins.bubbleRenderer.highlightLabelCanvas);
  552. plot.plugins.bubbleRenderer.highlightLabel.addClass('jqplot-bubble-label-highlight');
  553. }
  554. }
  555. function unhighlight (plot) {
  556. var canvas = plot.plugins.bubbleRenderer.highlightCanvas;
  557. var sidx = plot.plugins.bubbleRenderer.highlightedSeriesIndex;
  558. plot.plugins.bubbleRenderer.highlightLabelCanvas.empty();
  559. canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
  560. for (var i=0; i<plot.series.length; i++) {
  561. plot.series[i]._highlightedPoint = null;
  562. }
  563. plot.plugins.bubbleRenderer.highlightedSeriesIndex = null;
  564. plot.target.trigger('jqplotDataUnhighlight');
  565. }
  566. function handleMove(ev, gridpos, datapos, neighbor, plot) {
  567. if (neighbor) {
  568. var si = neighbor.seriesIndex;
  569. var pi = neighbor.pointIndex;
  570. var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]];
  571. var evt1 = jQuery.Event('jqplotDataMouseOver');
  572. evt1.pageX = ev.pageX;
  573. evt1.pageY = ev.pageY;
  574. plot.target.trigger(evt1, ins);
  575. if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.bubbleRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
  576. var evt = jQuery.Event('jqplotDataHighlight');
  577. evt.pageX = ev.pageX;
  578. evt.pageY = ev.pageY;
  579. plot.target.trigger(evt, ins);
  580. highlight (plot, ins[0], ins[1]);
  581. }
  582. }
  583. else if (neighbor == null) {
  584. unhighlight (plot);
  585. }
  586. }
  587. function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
  588. if (neighbor) {
  589. var si = neighbor.seriesIndex;
  590. var pi = neighbor.pointIndex;
  591. var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]];
  592. if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.bubbleRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
  593. var evt = jQuery.Event('jqplotDataHighlight');
  594. evt.pageX = ev.pageX;
  595. evt.pageY = ev.pageY;
  596. plot.target.trigger(evt, ins);
  597. highlight (plot, ins[0], ins[1]);
  598. }
  599. }
  600. else if (neighbor == null) {
  601. unhighlight (plot);
  602. }
  603. }
  604. function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
  605. var idx = plot.plugins.bubbleRenderer.highlightedSeriesIndex;
  606. if (idx != null && plot.series[idx].highlightMouseDown) {
  607. unhighlight(plot);
  608. }
  609. }
  610. function handleClick(ev, gridpos, datapos, neighbor, plot) {
  611. if (neighbor) {
  612. var si = neighbor.seriesIndex;
  613. var pi = neighbor.pointIndex;
  614. var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]];
  615. var evt = jQuery.Event('jqplotDataClick');
  616. evt.pageX = ev.pageX;
  617. evt.pageY = ev.pageY;
  618. plot.target.trigger(evt, ins);
  619. }
  620. }
  621. function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
  622. if (neighbor) {
  623. var si = neighbor.seriesIndex;
  624. var pi = neighbor.pointIndex;
  625. var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]];
  626. var idx = plot.plugins.bubbleRenderer.highlightedSeriesIndex;
  627. if (idx != null && plot.series[idx].highlightMouseDown) {
  628. unhighlight(plot);
  629. }
  630. var evt = jQuery.Event('jqplotDataRightClick');
  631. evt.pageX = ev.pageX;
  632. evt.pageY = ev.pageY;
  633. plot.target.trigger(evt, ins);
  634. }
  635. }
  636. // called within context of plot
  637. // create a canvas which we can draw on.
  638. // insert it before the eventCanvas, so eventCanvas will still capture events.
  639. function postPlotDraw() {
  640. // Memory Leaks patch
  641. if (this.plugins.bubbleRenderer && this.plugins.bubbleRenderer.highlightCanvas) {
  642. this.plugins.bubbleRenderer.highlightCanvas.resetCanvas();
  643. this.plugins.bubbleRenderer.highlightCanvas = null;
  644. }
  645. this.plugins.bubbleRenderer = {highlightedSeriesIndex:null};
  646. this.plugins.bubbleRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
  647. this.plugins.bubbleRenderer.highlightLabel = null;
  648. this.plugins.bubbleRenderer.highlightLabelCanvas = $('<div style="position:absolute;"></div>');
  649. var top = this._gridPadding.top;
  650. var left = this._gridPadding.left;
  651. var width = this._plotDimensions.width - this._gridPadding.left - this._gridPadding.right;
  652. var height = this._plotDimensions.height - this._gridPadding.top - this._gridPadding.bottom;
  653. this.plugins.bubbleRenderer.highlightLabelCanvas.css({top:top, left:left, width:width+'px', height:height+'px'});
  654. this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-bubbleRenderer-highlight-canvas', this._plotDimensions, this));
  655. this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightLabelCanvas);
  656. var hctx = this.plugins.bubbleRenderer.highlightCanvas.setContext();
  657. }
  658. // setup default renderers for axes and legend so user doesn't have to
  659. // called with scope of plot
  660. function preInit(target, data, options) {
  661. options = options || {};
  662. options.axesDefaults = options.axesDefaults || {};
  663. options.seriesDefaults = options.seriesDefaults || {};
  664. // only set these if there is a Bubble series
  665. var setopts = false;
  666. if (options.seriesDefaults.renderer == $.jqplot.BubbleRenderer) {
  667. setopts = true;
  668. }
  669. else if (options.series) {
  670. for (var i=0; i < options.series.length; i++) {
  671. if (options.series[i].renderer == $.jqplot.BubbleRenderer) {
  672. setopts = true;
  673. }
  674. }
  675. }
  676. if (setopts) {
  677. options.axesDefaults.renderer = $.jqplot.BubbleAxisRenderer;
  678. options.sortData = false;
  679. }
  680. }
  681. $.jqplot.preInitHooks.push(preInit);
  682. })(jQuery);