jqplot.bubbleRenderer.js 30 KB

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