jqplot.cursor.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  1. /**
  2. * jqPlot
  3. * Pure JavaScript plotting plugin using jQuery
  4. *
  5. * Version: 1.0.9
  6. * Revision: dff2f04
  7. *
  8. * Copyright (c) 2009-2016 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.Cursor
  34. * Plugin class representing the cursor as displayed on the plot.
  35. */
  36. $.jqplot.Cursor = function(options) {
  37. // Group: Properties
  38. //
  39. // prop: style
  40. // CSS spec for cursor style
  41. this.style = 'crosshair';
  42. this.previousCursor = 'auto';
  43. // prop: show
  44. // whether to show the cursor or not.
  45. this.show = $.jqplot.config.enablePlugins;
  46. // prop: showTooltip
  47. // show a cursor position tooltip. Location of the tooltip
  48. // will be controlled by followMouse and tooltipLocation.
  49. this.showTooltip = true;
  50. // prop: followMouse
  51. // Tooltip follows the mouse, it is not at a fixed location.
  52. // Tooltip will show on the grid at the location given by
  53. // tooltipLocation, offset from the grid edge by tooltipOffset.
  54. this.followMouse = false;
  55. // prop: tooltipLocation
  56. // Where to position tooltip. If followMouse is true, this is
  57. // relative to the cursor, otherwise, it is relative to the grid.
  58. // One of 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
  59. this.tooltipLocation = 'se';
  60. // prop: tooltipOffset
  61. // Pixel offset of tooltip from the grid boudaries or cursor center.
  62. this.tooltipOffset = 6;
  63. // prop: showTooltipGridPosition
  64. // show the grid pixel coordinates of the mouse.
  65. this.showTooltipGridPosition = false;
  66. // prop: showTooltipUnitPosition
  67. // show the unit (data) coordinates of the mouse.
  68. this.showTooltipUnitPosition = true;
  69. // prop: showTooltipDataPosition
  70. // Used with showVerticalLine to show intersecting data points in the tooltip.
  71. this.showTooltipDataPosition = false;
  72. // prop: tooltipFormatString
  73. // sprintf format string for the tooltip.
  74. // Uses Ash Searle's javascript sprintf implementation
  75. // found here: http://hexmen.com/blog/2007/03/printf-sprintf/
  76. // See http://perldoc.perl.org/functions/sprintf.html for reference
  77. // Note, if showTooltipDataPosition is true, the default tooltipFormatString
  78. // will be set to the cursorLegendFormatString, not the default given here.
  79. this.tooltipFormatString = '%.4P, %.4P';
  80. // prop: useAxesFormatters
  81. // Use the x and y axes formatters to format the text in the tooltip.
  82. this.useAxesFormatters = true;
  83. // prop: tooltipAxisGroups
  84. // Show position for the specified axes.
  85. // This is an array like [['xaxis', 'yaxis'], ['xaxis', 'y2axis']]
  86. // Default is to compute automatically for all visible axes.
  87. this.tooltipAxisGroups = [];
  88. // prop: zoom
  89. // Enable plot zooming.
  90. this.zoom = false;
  91. // zoomProxy and zoomTarget properties are not directly set by user.
  92. // They Will be set through call to zoomProxy method.
  93. this.zoomProxy = false;
  94. this.zoomTarget = false;
  95. // prop: looseZoom
  96. // Will expand zoom range to provide more rounded tick values.
  97. // Works only with linear, log and date axes.
  98. this.looseZoom = true;
  99. // prop: clickReset
  100. // Will reset plot zoom if single click on plot without drag.
  101. this.clickReset = false;
  102. // prop: dblClickReset
  103. // Will reset plot zoom if double click on plot without drag.
  104. this.dblClickReset = true;
  105. // prop: showVerticalLine
  106. // draw a vertical line across the plot which follows the cursor.
  107. // When the line is near a data point, a special legend and/or tooltip can
  108. // be updated with the data values.
  109. this.showVerticalLine = false;
  110. // prop: showHorizontalLine
  111. // draw a horizontal line across the plot which follows the cursor.
  112. this.showHorizontalLine = false;
  113. // prop: constrainZoomTo
  114. // 'none', 'x' or 'y'
  115. this.constrainZoomTo = 'none';
  116. // // prop: autoscaleConstraint
  117. // // when a constrained axis is specified, true will
  118. // // auatoscale the adjacent axis.
  119. // this.autoscaleConstraint = true;
  120. this.shapeRenderer = new $.jqplot.ShapeRenderer();
  121. this._zoom = {start:[], end:[], started: false, zooming:false, isZoomed:false, axes:{start:{}, end:{}}, gridpos:{}, datapos:{}};
  122. this._tooltipElem;
  123. this.zoomCanvas;
  124. this.cursorCanvas;
  125. // prop: intersectionThreshold
  126. // pixel distance from data point or marker to consider cursor lines intersecting with point.
  127. // If data point markers are not shown, this should be >= 1 or will often miss point intersections.
  128. this.intersectionThreshold = 2;
  129. // prop: showCursorLegend
  130. // Replace the plot legend with an enhanced legend displaying intersection information.
  131. this.showCursorLegend = false;
  132. // prop: cursorLegendFormatString
  133. // Format string used in the cursor legend. If showTooltipDataPosition is true,
  134. // this will also be the default format string used by tooltipFormatString.
  135. this.cursorLegendFormatString = $.jqplot.Cursor.cursorLegendFormatString;
  136. // whether the cursor is over the grid or not.
  137. this._oldHandlers = {onselectstart: null, ondrag: null, onmousedown: null};
  138. // prop: constrainOutsideZoom
  139. // True to limit actual zoom area to edges of grid, even when zooming
  140. // outside of plot area. That is, can't zoom out by mousing outside plot.
  141. this.constrainOutsideZoom = true;
  142. // prop: showTooltipOutsideZoom
  143. // True will keep updating the tooltip when zooming of the grid.
  144. this.showTooltipOutsideZoom = false;
  145. // true if mouse is over grid, false if not.
  146. this.onGrid = false;
  147. $.extend(true, this, options);
  148. };
  149. $.jqplot.Cursor.cursorLegendFormatString = '%s x:%s, y:%s';
  150. // called with scope of plot
  151. $.jqplot.Cursor.init = function (target, data, opts){
  152. // add a cursor attribute to the plot
  153. var options = opts || {};
  154. this.plugins.cursor = new $.jqplot.Cursor(options.cursor);
  155. var c = this.plugins.cursor;
  156. if (c.show) {
  157. $.jqplot.eventListenerHooks.push(['jqplotMouseEnter', handleMouseEnter]);
  158. $.jqplot.eventListenerHooks.push(['jqplotMouseLeave', handleMouseLeave]);
  159. $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMouseMove]);
  160. if (c.showCursorLegend) {
  161. opts.legend = opts.legend || {};
  162. opts.legend.renderer = $.jqplot.CursorLegendRenderer;
  163. opts.legend.formatString = this.plugins.cursor.cursorLegendFormatString;
  164. opts.legend.show = true;
  165. }
  166. if (c.zoom) {
  167. $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleMouseDown]);
  168. if (c.clickReset) {
  169. $.jqplot.eventListenerHooks.push(['jqplotClick', handleClick]);
  170. }
  171. if (c.dblClickReset) {
  172. $.jqplot.eventListenerHooks.push(['jqplotDblClick', handleDblClick]);
  173. }
  174. }
  175. this.resetZoom = function() {
  176. var axes = this.axes;
  177. if (!c.zoomProxy) {
  178. for (var ax in axes) {
  179. axes[ax].reset();
  180. axes[ax]._ticks = [];
  181. // fake out tick creation algorithm to make sure original auto
  182. // computed format string is used if _overrideFormatString is true
  183. if (c._zoom.axes[ax] !== undefined) {
  184. axes[ax]._autoFormatString = c._zoom.axes[ax].tickFormatString;
  185. }
  186. }
  187. this.redraw();
  188. }
  189. else {
  190. var ctx = this.plugins.cursor.zoomCanvas._ctx;
  191. ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
  192. ctx = null;
  193. }
  194. this.plugins.cursor._zoom.isZoomed = false;
  195. this.target.trigger('jqplotResetZoom', [this, this.plugins.cursor]);
  196. };
  197. if (c.showTooltipDataPosition) {
  198. c.showTooltipUnitPosition = false;
  199. c.showTooltipGridPosition = false;
  200. if (options.cursor.tooltipFormatString == undefined) {
  201. c.tooltipFormatString = $.jqplot.Cursor.cursorLegendFormatString;
  202. }
  203. }
  204. }
  205. };
  206. // called with context of plot
  207. $.jqplot.Cursor.postDraw = function() {
  208. var c = this.plugins.cursor;
  209. // Memory Leaks patch
  210. if (c.zoomCanvas) {
  211. c.zoomCanvas.resetCanvas();
  212. c.zoomCanvas = null;
  213. }
  214. if (c.cursorCanvas) {
  215. c.cursorCanvas.resetCanvas();
  216. c.cursorCanvas = null;
  217. }
  218. if (c._tooltipElem) {
  219. c._tooltipElem.emptyForce();
  220. c._tooltipElem = null;
  221. }
  222. if (c.zoom) {
  223. c.zoomCanvas = new $.jqplot.GenericCanvas();
  224. this.eventCanvas._elem.before(c.zoomCanvas.createElement(this._gridPadding, 'jqplot-zoom-canvas', this._plotDimensions, this));
  225. c.zoomCanvas.setContext();
  226. }
  227. var elem = document.createElement('div');
  228. c._tooltipElem = $(elem);
  229. elem = null;
  230. c._tooltipElem.addClass('jqplot-cursor-tooltip');
  231. c._tooltipElem.css({position:'absolute', display:'none'});
  232. if (c.zoomCanvas) {
  233. c.zoomCanvas._elem.before(c._tooltipElem);
  234. }
  235. else {
  236. this.eventCanvas._elem.before(c._tooltipElem);
  237. }
  238. if (c.showVerticalLine || c.showHorizontalLine) {
  239. c.cursorCanvas = new $.jqplot.GenericCanvas();
  240. this.eventCanvas._elem.before(c.cursorCanvas.createElement(this._gridPadding, 'jqplot-cursor-canvas', this._plotDimensions, this));
  241. c.cursorCanvas.setContext();
  242. }
  243. // if we are showing the positions in unit coordinates, and no axes groups
  244. // were specified, create a default set.
  245. if (c.showTooltipUnitPosition){
  246. if (c.tooltipAxisGroups.length === 0) {
  247. var series = this.series;
  248. var s;
  249. var temp = [];
  250. for (var i=0; i<series.length; i++) {
  251. s = series[i];
  252. var ax = s.xaxis+','+s.yaxis;
  253. if ($.inArray(ax, temp) == -1) {
  254. temp.push(ax);
  255. }
  256. }
  257. for (var i=0; i<temp.length; i++) {
  258. c.tooltipAxisGroups.push(temp[i].split(','));
  259. }
  260. }
  261. }
  262. };
  263. // Group: methods
  264. //
  265. // method: $.jqplot.Cursor.zoomProxy
  266. // links targetPlot to controllerPlot so that plot zooming of
  267. // targetPlot will be controlled by zooming on the controllerPlot.
  268. // controllerPlot will not actually zoom, but acts as an
  269. // overview plot. Note, the zoom options must be set to true for
  270. // zoomProxy to work.
  271. $.jqplot.Cursor.zoomProxy = function(targetPlot, controllerPlot) {
  272. var tc = targetPlot.plugins.cursor;
  273. var cc = controllerPlot.plugins.cursor;
  274. tc.zoomTarget = true;
  275. tc.zoom = true;
  276. tc.style = 'auto';
  277. tc.dblClickReset = false;
  278. cc.zoom = true;
  279. cc.zoomProxy = true;
  280. controllerPlot.target.bind('jqplotZoom', plotZoom);
  281. controllerPlot.target.bind('jqplotResetZoom', plotReset);
  282. function plotZoom(ev, gridpos, datapos, plot, cursor) {
  283. tc.doZoom(gridpos, datapos, targetPlot, cursor);
  284. }
  285. function plotReset(ev, plot, cursor) {
  286. targetPlot.resetZoom();
  287. }
  288. };
  289. $.jqplot.Cursor.prototype.resetZoom = function(plot, cursor) {
  290. var axes = plot.axes;
  291. var cax = cursor._zoom.axes;
  292. if (!plot.plugins.cursor.zoomProxy && cursor._zoom.isZoomed) {
  293. for (var ax in axes) {
  294. // axes[ax]._ticks = [];
  295. // axes[ax].min = cax[ax].min;
  296. // axes[ax].max = cax[ax].max;
  297. // axes[ax].numberTicks = cax[ax].numberTicks;
  298. // axes[ax].tickInterval = cax[ax].tickInterval;
  299. // // for date axes
  300. // axes[ax].daTickInterval = cax[ax].daTickInterval;
  301. axes[ax].reset();
  302. axes[ax]._ticks = [];
  303. // fake out tick creation algorithm to make sure original auto
  304. // computed format string is used if _overrideFormatString is true
  305. axes[ax]._autoFormatString = cax[ax].tickFormatString;
  306. }
  307. plot.redraw();
  308. cursor._zoom.isZoomed = false;
  309. }
  310. else {
  311. var ctx = cursor.zoomCanvas._ctx;
  312. ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
  313. ctx = null;
  314. }
  315. plot.target.trigger('jqplotResetZoom', [plot, cursor]);
  316. };
  317. $.jqplot.Cursor.resetZoom = function(plot) {
  318. plot.resetZoom();
  319. };
  320. $.jqplot.Cursor.prototype.doZoom = function (gridpos, datapos, plot, cursor) {
  321. var c = cursor;
  322. var axes = plot.axes;
  323. var zaxes = c._zoom.axes;
  324. var start = zaxes.start;
  325. var end = zaxes.end;
  326. var min, max, dp, span,
  327. newmin, newmax, curax, _numberTicks, ret;
  328. var ctx = plot.plugins.cursor.zoomCanvas._ctx;
  329. // don't zoom if zoom area is too small (in pixels)
  330. if ((c.constrainZoomTo == 'none' && Math.abs(gridpos.x - c._zoom.start[0]) > 6 && Math.abs(gridpos.y - c._zoom.start[1]) > 6) || (c.constrainZoomTo == 'x' && Math.abs(gridpos.x - c._zoom.start[0]) > 6) || (c.constrainZoomTo == 'y' && Math.abs(gridpos.y - c._zoom.start[1]) > 6)) {
  331. if (!plot.plugins.cursor.zoomProxy) {
  332. for (var ax in datapos) {
  333. // make a copy of the original axes to revert back.
  334. if (c._zoom.axes[ax] == undefined) {
  335. c._zoom.axes[ax] = {};
  336. c._zoom.axes[ax].numberTicks = axes[ax].numberTicks;
  337. c._zoom.axes[ax].tickInterval = axes[ax].tickInterval;
  338. // for date axes...
  339. c._zoom.axes[ax].daTickInterval = axes[ax].daTickInterval;
  340. c._zoom.axes[ax].min = axes[ax].min;
  341. c._zoom.axes[ax].max = axes[ax].max;
  342. c._zoom.axes[ax].tickFormatString = (axes[ax].tickOptions != null) ? axes[ax].tickOptions.formatString : '';
  343. }
  344. if ((c.constrainZoomTo == 'none') || (c.constrainZoomTo == 'x' && ax.charAt(0) == 'x') || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'y')) {
  345. dp = datapos[ax];
  346. if (dp != null) {
  347. if (dp > start[ax]) {
  348. newmin = start[ax];
  349. newmax = dp;
  350. }
  351. else {
  352. span = start[ax] - dp;
  353. newmin = dp;
  354. newmax = start[ax];
  355. }
  356. curax = axes[ax];
  357. _numberTicks = null;
  358. // if aligning this axis, use number of ticks from previous axis.
  359. // Do I need to reset somehow if alignTicks is changed and then graph is replotted??
  360. if (curax.alignTicks) {
  361. if (curax.name === 'x2axis' && plot.axes.xaxis.show) {
  362. _numberTicks = plot.axes.xaxis.numberTicks;
  363. }
  364. else if (curax.name.charAt(0) === 'y' && curax.name !== 'yaxis' && curax.name !== 'yMidAxis' && plot.axes.yaxis.show) {
  365. _numberTicks = plot.axes.yaxis.numberTicks;
  366. }
  367. }
  368. if (this.looseZoom && (axes[ax].renderer.constructor === $.jqplot.LinearAxisRenderer || axes[ax].renderer.constructor === $.jqplot.LogAxisRenderer )) { //} || axes[ax].renderer.constructor === $.jqplot.DateAxisRenderer)) {
  369. ret = $.jqplot.LinearTickGenerator(newmin, newmax, curax._scalefact, _numberTicks);
  370. // if new minimum is less than "true" minimum of axis display, adjust it
  371. if (axes[ax].tickInset && ret[0] < axes[ax].min + axes[ax].tickInset * axes[ax].tickInterval) {
  372. ret[0] += ret[4];
  373. ret[2] -= 1;
  374. }
  375. // if new maximum is greater than "true" max of axis display, adjust it
  376. if (axes[ax].tickInset && ret[1] > axes[ax].max - axes[ax].tickInset * axes[ax].tickInterval) {
  377. ret[1] -= ret[4];
  378. ret[2] -= 1;
  379. }
  380. // for log axes, don't fall below current minimum, this will look bad and can't have 0 in range anyway.
  381. if (axes[ax].renderer.constructor === $.jqplot.LogAxisRenderer && ret[0] < axes[ax].min) {
  382. // remove a tick and shift min up
  383. ret[0] += ret[4];
  384. ret[2] -= 1;
  385. }
  386. axes[ax].min = ret[0];
  387. axes[ax].max = ret[1];
  388. axes[ax]._autoFormatString = ret[3];
  389. axes[ax].numberTicks = ret[2];
  390. axes[ax].tickInterval = ret[4];
  391. // for date axes...
  392. axes[ax].daTickInterval = [ret[4]/1000, 'seconds'];
  393. }
  394. else {
  395. axes[ax].min = newmin;
  396. axes[ax].max = newmax;
  397. axes[ax].tickInterval = null;
  398. axes[ax].numberTicks = null;
  399. // for date axes...
  400. axes[ax].daTickInterval = null;
  401. }
  402. axes[ax]._ticks = [];
  403. }
  404. }
  405. }
  406. for (var ax in datapos) {
  407. if ((c.constrainZoomTo == 'x' && ax.charAt(0) == 'y' && c.autoscaleConstraint)) {
  408. dp = datapos[ax];
  409. if (dp != null) {
  410. curax = axes[ax];
  411. curax.min = axes[ax]._options.min;
  412. curax.max = axes[ax]._options.max;
  413. if (axes[ax]._options.min == null || axes[ax]._options.max == null) {
  414. var seriesMin = null;
  415. var seriesMax = null;
  416. $.each(plot.series, function(seriesIdx, seriesObj) {
  417. if (seriesObj.yaxis == ax) {
  418. var xaxis = axes[seriesObj.xaxis];
  419. var i;
  420. var d = seriesObj._plotData;
  421. for (i = 0; i < d.length; i++) {
  422. var point = d[i];
  423. if (point[0] >= xaxis.min && point[0] <= xaxis.max) {
  424. if (seriesMin == null || point[1] < seriesMin) {
  425. seriesMin = point[1];
  426. }
  427. if (seriesMax == null || point[1] > seriesMax) {
  428. seriesMax = point[1];
  429. }
  430. }
  431. }
  432. }
  433. });
  434. if (axes[ax]._options.min != null) {
  435. seriesMin = axes[ax]._options.min;
  436. }
  437. if (axes[ax]._options.max != null) {
  438. seriesMax = axes[ax]._options.max;
  439. }
  440. var r = $.jqplot.LinearTickGenerator(seriesMin, seriesMax, null, null, (axes[ax]._options.min != null), (axes[ax]._options.max != null));
  441. curax.min = r[0];
  442. curax.max = r[1];
  443. curax.tickInterval = null;
  444. curax.numberTicks = null;
  445. }
  446. }
  447. }
  448. }
  449. ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
  450. plot.redraw();
  451. c._zoom.isZoomed = true;
  452. ctx = null;
  453. }
  454. plot.target.trigger('jqplotZoom', [gridpos, datapos, plot, cursor]);
  455. }
  456. };
  457. $.jqplot.preInitHooks.push($.jqplot.Cursor.init);
  458. $.jqplot.postDrawHooks.push($.jqplot.Cursor.postDraw);
  459. function updateTooltip(gridpos, datapos, plot) {
  460. var c = plot.plugins.cursor;
  461. var s = '';
  462. var addbr = false;
  463. if (c.showTooltipGridPosition) {
  464. s = gridpos.x+', '+gridpos.y;
  465. addbr = true;
  466. }
  467. if (c.showTooltipUnitPosition) {
  468. var g;
  469. for (var i=0; i<c.tooltipAxisGroups.length; i++) {
  470. g = c.tooltipAxisGroups[i];
  471. if (addbr) {
  472. s += '<br />';
  473. }
  474. if (c.useAxesFormatters) {
  475. for (var j=0; j<g.length; j++) {
  476. if (j) {
  477. s += ', ';
  478. }
  479. var af = plot.axes[g[j]]._ticks[0].formatter;
  480. var afstr = plot.axes[g[j]]._ticks[0].formatString;
  481. s += af(afstr, datapos[g[j]]);
  482. }
  483. }
  484. else {
  485. s += $.jqplot.sprintf(c.tooltipFormatString, datapos[g[0]], datapos[g[1]]);
  486. }
  487. addbr = true;
  488. }
  489. }
  490. if (c.showTooltipDataPosition) {
  491. var series = plot.series;
  492. var ret = getIntersectingPoints(plot, gridpos.x, gridpos.y);
  493. var addbr = false;
  494. for (var i = 0; i< series.length; i++) {
  495. if (series[i].show) {
  496. var idx = series[i].index;
  497. var label = series[i].label.toString();
  498. var cellid = $.inArray(idx, ret.indices);
  499. var sx = undefined;
  500. var sy = undefined;
  501. if (cellid != -1) {
  502. var data = ret.data[cellid].data;
  503. if (c.useAxesFormatters) {
  504. var xf = series[i]._xaxis._ticks[0].formatter;
  505. var yf = series[i]._yaxis._ticks[0].formatter;
  506. var xfstr = series[i]._xaxis._ticks[0].formatString;
  507. var yfstr = series[i]._yaxis._ticks[0].formatString;
  508. sx = xf(xfstr, data[0]);
  509. sy = yf(yfstr, data[1]);
  510. }
  511. else {
  512. sx = data[0];
  513. sy = data[1];
  514. }
  515. if (addbr) {
  516. s += '<br />';
  517. }
  518. s += $.jqplot.sprintf(c.tooltipFormatString, label, sx, sy);
  519. addbr = true;
  520. }
  521. }
  522. }
  523. }
  524. c._tooltipElem.html(s);
  525. }
  526. function moveLine(gridpos, plot) {
  527. var c = plot.plugins.cursor;
  528. var ctx = c.cursorCanvas._ctx;
  529. ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
  530. if (c.showVerticalLine) {
  531. c.shapeRenderer.draw(ctx, [[gridpos.x, 0], [gridpos.x, ctx.canvas.height]]);
  532. }
  533. if (c.showHorizontalLine) {
  534. c.shapeRenderer.draw(ctx, [[0, gridpos.y], [ctx.canvas.width, gridpos.y]]);
  535. }
  536. var ret = getIntersectingPoints(plot, gridpos.x, gridpos.y);
  537. if (c.showCursorLegend) {
  538. var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label');
  539. for (var i=0; i<cells.length; i++) {
  540. var idx = $(cells[i]).data('seriesIndex');
  541. var series = plot.series[idx];
  542. var label = series.label.toString();
  543. var cellid = $.inArray(idx, ret.indices);
  544. var sx = undefined;
  545. var sy = undefined;
  546. if (cellid != -1) {
  547. var data = ret.data[cellid].data;
  548. if (c.useAxesFormatters) {
  549. var xf = series._xaxis._ticks[0].formatter;
  550. var yf = series._yaxis._ticks[0].formatter;
  551. var xfstr = series._xaxis._ticks[0].formatString;
  552. var yfstr = series._yaxis._ticks[0].formatString;
  553. sx = xf(xfstr, data[0]);
  554. sy = yf(yfstr, data[1]);
  555. }
  556. else {
  557. sx = data[0];
  558. sy = data[1];
  559. }
  560. }
  561. if (plot.legend.escapeHtml) {
  562. $(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, sx, sy));
  563. }
  564. else {
  565. $(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, sx, sy));
  566. }
  567. }
  568. }
  569. ctx = null;
  570. }
  571. function getIntersectingPoints(plot, x, y) {
  572. var ret = {indices:[], data:[]};
  573. var s, i, d0, d, j, r, p;
  574. var threshold;
  575. var c = plot.plugins.cursor;
  576. for (var i=0; i<plot.series.length; i++) {
  577. s = plot.series[i];
  578. r = s.renderer;
  579. if (s.show) {
  580. threshold = c.intersectionThreshold;
  581. if (s.showMarker) {
  582. threshold += s.markerRenderer.size/2;
  583. }
  584. for (var j=0; j<s.gridData.length; j++) {
  585. p = s.gridData[j];
  586. // check vertical line
  587. if (c.showVerticalLine) {
  588. if (Math.abs(x-p[0]) <= threshold) {
  589. ret.indices.push(i);
  590. ret.data.push({seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]});
  591. }
  592. }
  593. }
  594. }
  595. }
  596. return ret;
  597. }
  598. function moveTooltip(gridpos, plot) {
  599. var c = plot.plugins.cursor;
  600. var elem = c._tooltipElem;
  601. switch (c.tooltipLocation) {
  602. case 'nw':
  603. var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset;
  604. var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true);
  605. break;
  606. case 'n':
  607. var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
  608. var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true);
  609. break;
  610. case 'ne':
  611. var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
  612. var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true);
  613. break;
  614. case 'e':
  615. var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
  616. var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
  617. break;
  618. case 'se':
  619. var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
  620. var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
  621. break;
  622. case 's':
  623. var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
  624. var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
  625. break;
  626. case 'sw':
  627. var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset;
  628. var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
  629. break;
  630. case 'w':
  631. var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset;
  632. var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
  633. break;
  634. default:
  635. var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset;
  636. var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset;
  637. break;
  638. }
  639. elem.css('left', x);
  640. elem.css('top', y);
  641. elem = null;
  642. }
  643. function positionTooltip(plot) {
  644. // fake a grid for positioning
  645. var grid = plot._gridPadding;
  646. var c = plot.plugins.cursor;
  647. var elem = c._tooltipElem;
  648. switch (c.tooltipLocation) {
  649. case 'nw':
  650. var a = grid.left + c.tooltipOffset;
  651. var b = grid.top + c.tooltipOffset;
  652. elem.css('left', a);
  653. elem.css('top', b);
  654. break;
  655. case 'n':
  656. var a = (grid.left + (plot._plotDimensions.width - grid.right))/2 - elem.outerWidth(true)/2;
  657. var b = grid.top + c.tooltipOffset;
  658. elem.css('left', a);
  659. elem.css('top', b);
  660. break;
  661. case 'ne':
  662. var a = grid.right + c.tooltipOffset;
  663. var b = grid.top + c.tooltipOffset;
  664. elem.css({right:a, top:b});
  665. break;
  666. case 'e':
  667. var a = grid.right + c.tooltipOffset;
  668. var b = (grid.top + (plot._plotDimensions.height - grid.bottom))/2 - elem.outerHeight(true)/2;
  669. elem.css({right:a, top:b});
  670. break;
  671. case 'se':
  672. var a = grid.right + c.tooltipOffset;
  673. var b = grid.bottom + c.tooltipOffset;
  674. elem.css({right:a, bottom:b});
  675. break;
  676. case 's':
  677. var a = (grid.left + (plot._plotDimensions.width - grid.right))/2 - elem.outerWidth(true)/2;
  678. var b = grid.bottom + c.tooltipOffset;
  679. elem.css({left:a, bottom:b});
  680. break;
  681. case 'sw':
  682. var a = grid.left + c.tooltipOffset;
  683. var b = grid.bottom + c.tooltipOffset;
  684. elem.css({left:a, bottom:b});
  685. break;
  686. case 'w':
  687. var a = grid.left + c.tooltipOffset;
  688. var b = (grid.top + (plot._plotDimensions.height - grid.bottom))/2 - elem.outerHeight(true)/2;
  689. elem.css({left:a, top:b});
  690. break;
  691. default: // same as 'se'
  692. var a = grid.right - c.tooltipOffset;
  693. var b = grid.bottom + c.tooltipOffset;
  694. elem.css({right:a, bottom:b});
  695. break;
  696. }
  697. elem = null;
  698. }
  699. function handleClick (ev, gridpos, datapos, neighbor, plot) {
  700. ev.preventDefault();
  701. ev.stopImmediatePropagation();
  702. var c = plot.plugins.cursor;
  703. if (c.clickReset) {
  704. c.resetZoom(plot, c);
  705. }
  706. var sel = window.getSelection;
  707. if (document.selection && document.selection.empty)
  708. {
  709. document.selection.empty();
  710. }
  711. else if (sel && !sel().isCollapsed) {
  712. sel().collapse();
  713. }
  714. return false;
  715. }
  716. function handleDblClick (ev, gridpos, datapos, neighbor, plot) {
  717. ev.preventDefault();
  718. ev.stopImmediatePropagation();
  719. var c = plot.plugins.cursor;
  720. if (c.dblClickReset) {
  721. c.resetZoom(plot, c);
  722. }
  723. var sel = window.getSelection;
  724. if (document.selection && document.selection.empty)
  725. {
  726. document.selection.empty();
  727. }
  728. else if (sel && !sel().isCollapsed) {
  729. sel().collapse();
  730. }
  731. return false;
  732. }
  733. function handleMouseLeave(ev, gridpos, datapos, neighbor, plot) {
  734. var c = plot.plugins.cursor;
  735. c.onGrid = false;
  736. if (c.show) {
  737. $(ev.target).css('cursor', c.previousCursor);
  738. if (c.showTooltip && !(c._zoom.zooming && c.showTooltipOutsideZoom && !c.constrainOutsideZoom)) {
  739. c._tooltipElem.empty();
  740. c._tooltipElem.hide();
  741. }
  742. if (c.zoom) {
  743. c._zoom.gridpos = gridpos;
  744. c._zoom.datapos = datapos;
  745. }
  746. if (c.showVerticalLine || c.showHorizontalLine) {
  747. var ctx = c.cursorCanvas._ctx;
  748. ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
  749. ctx = null;
  750. }
  751. if (c.showCursorLegend) {
  752. var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label');
  753. for (var i=0; i<cells.length; i++) {
  754. var idx = $(cells[i]).data('seriesIndex');
  755. var series = plot.series[idx];
  756. var label = series.label.toString();
  757. if (plot.legend.escapeHtml) {
  758. $(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined));
  759. }
  760. else {
  761. $(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined));
  762. }
  763. }
  764. }
  765. }
  766. }
  767. function handleMouseEnter(ev, gridpos, datapos, neighbor, plot) {
  768. var c = plot.plugins.cursor;
  769. c.onGrid = true;
  770. if (c.show) {
  771. c.previousCursor = ev.target.style.cursor;
  772. ev.target.style.cursor = c.style;
  773. if (c.showTooltip) {
  774. updateTooltip(gridpos, datapos, plot);
  775. if (c.followMouse) {
  776. moveTooltip(gridpos, plot);
  777. }
  778. else {
  779. positionTooltip(plot);
  780. }
  781. c._tooltipElem.show();
  782. }
  783. if (c.showVerticalLine || c.showHorizontalLine) {
  784. moveLine(gridpos, plot);
  785. }
  786. }
  787. }
  788. function handleMouseMove(ev, gridpos, datapos, neighbor, plot) {
  789. var c = plot.plugins.cursor;
  790. if (c.show) {
  791. if (c.showTooltip) {
  792. updateTooltip(gridpos, datapos, plot);
  793. if (c.followMouse) {
  794. moveTooltip(gridpos, plot);
  795. }
  796. }
  797. if (c.showVerticalLine || c.showHorizontalLine) {
  798. moveLine(gridpos, plot);
  799. }
  800. }
  801. }
  802. function getEventPosition(ev) {
  803. var plot = ev.data.plot;
  804. var go = plot.eventCanvas._elem.offset();
  805. var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top};
  806. //////
  807. // TO DO: handle yMidAxis
  808. //////
  809. var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null, yMidAxis:null};
  810. var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis'];
  811. var ax = plot.axes;
  812. var n, axis;
  813. for (n=11; n>0; n--) {
  814. axis = an[n-1];
  815. if (ax[axis].show) {
  816. dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]);
  817. }
  818. }
  819. return {offsets:go, gridPos:gridPos, dataPos:dataPos};
  820. }
  821. function handleZoomMove(ev) {
  822. var plot = ev.data.plot;
  823. var c = plot.plugins.cursor;
  824. // don't do anything if not on grid.
  825. if (c.show && c.zoom && c._zoom.started && !c.zoomTarget) {
  826. ev.preventDefault();
  827. var ctx = c.zoomCanvas._ctx;
  828. var positions = getEventPosition(ev);
  829. var gridpos = positions.gridPos;
  830. var datapos = positions.dataPos;
  831. c._zoom.gridpos = gridpos;
  832. c._zoom.datapos = datapos;
  833. c._zoom.zooming = true;
  834. var xpos = gridpos.x;
  835. var ypos = gridpos.y;
  836. var height = ctx.canvas.height;
  837. var width = ctx.canvas.width;
  838. if (c.showTooltip && !c.onGrid && c.showTooltipOutsideZoom) {
  839. updateTooltip(gridpos, datapos, plot);
  840. if (c.followMouse) {
  841. moveTooltip(gridpos, plot);
  842. }
  843. }
  844. if (c.constrainZoomTo == 'x') {
  845. c._zoom.end = [xpos, height];
  846. }
  847. else if (c.constrainZoomTo == 'y') {
  848. c._zoom.end = [width, ypos];
  849. }
  850. else {
  851. c._zoom.end = [xpos, ypos];
  852. }
  853. var sel = window.getSelection;
  854. if (document.selection && document.selection.empty)
  855. {
  856. document.selection.empty();
  857. }
  858. else if (sel && !sel().isCollapsed) {
  859. sel().collapse();
  860. }
  861. drawZoomBox.call(c);
  862. ctx = null;
  863. }
  864. }
  865. function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
  866. var c = plot.plugins.cursor;
  867. if(plot.plugins.mobile){
  868. $(document).one('vmouseup.jqplot_cursor', {plot:plot}, handleMouseUp);
  869. } else {
  870. $(document).one('mouseup.jqplot_cursor', {plot:plot}, handleMouseUp);
  871. }
  872. var axes = plot.axes;
  873. if (document.onselectstart != undefined) {
  874. c._oldHandlers.onselectstart = document.onselectstart;
  875. document.onselectstart = function () { return false; };
  876. }
  877. if (document.ondrag != undefined) {
  878. c._oldHandlers.ondrag = document.ondrag;
  879. document.ondrag = function () { return false; };
  880. }
  881. if (document.onmousedown != undefined) {
  882. c._oldHandlers.onmousedown = document.onmousedown;
  883. document.onmousedown = function () { return false; };
  884. }
  885. if (c.zoom) {
  886. if (!c.zoomProxy) {
  887. var ctx = c.zoomCanvas._ctx;
  888. ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
  889. ctx = null;
  890. }
  891. if (c.constrainZoomTo == 'x') {
  892. c._zoom.start = [gridpos.x, 0];
  893. }
  894. else if (c.constrainZoomTo == 'y') {
  895. c._zoom.start = [0, gridpos.y];
  896. }
  897. else {
  898. c._zoom.start = [gridpos.x, gridpos.y];
  899. }
  900. c._zoom.started = true;
  901. for (var ax in datapos) {
  902. // get zoom starting position.
  903. c._zoom.axes.start[ax] = datapos[ax];
  904. }
  905. if(plot.plugins.mobile){
  906. $(document).bind('vmousemove.jqplotCursor', {plot:plot}, handleZoomMove);
  907. } else {
  908. $(document).bind('mousemove.jqplotCursor', {plot:plot}, handleZoomMove);
  909. }
  910. }
  911. }
  912. function handleMouseUp(ev) {
  913. var plot = ev.data.plot;
  914. var c = plot.plugins.cursor;
  915. if (c.zoom && c._zoom.zooming && !c.zoomTarget) {
  916. var xpos = c._zoom.gridpos.x;
  917. var ypos = c._zoom.gridpos.y;
  918. var datapos = c._zoom.datapos;
  919. var height = c.zoomCanvas._ctx.canvas.height;
  920. var width = c.zoomCanvas._ctx.canvas.width;
  921. var axes = plot.axes;
  922. if (c.constrainOutsideZoom && !c.onGrid) {
  923. if (xpos < 0) { xpos = 0; }
  924. else if (xpos > width) { xpos = width; }
  925. if (ypos < 0) { ypos = 0; }
  926. else if (ypos > height) { ypos = height; }
  927. for (var axis in datapos) {
  928. if (datapos[axis]) {
  929. if (axis.charAt(0) == 'x') {
  930. datapos[axis] = axes[axis].series_p2u(xpos);
  931. }
  932. else {
  933. datapos[axis] = axes[axis].series_p2u(ypos);
  934. }
  935. }
  936. }
  937. }
  938. if (c.constrainZoomTo == 'x') {
  939. ypos = height;
  940. }
  941. else if (c.constrainZoomTo == 'y') {
  942. xpos = width;
  943. }
  944. c._zoom.end = [xpos, ypos];
  945. c._zoom.gridpos = {x:xpos, y:ypos};
  946. c.doZoom(c._zoom.gridpos, datapos, plot, c);
  947. }
  948. c._zoom.started = false;
  949. c._zoom.zooming = false;
  950. $(document).unbind('mousemove.jqplotCursor', handleZoomMove);
  951. if (document.onselectstart != undefined && c._oldHandlers.onselectstart != null){
  952. document.onselectstart = c._oldHandlers.onselectstart;
  953. c._oldHandlers.onselectstart = null;
  954. }
  955. if (document.ondrag != undefined && c._oldHandlers.ondrag != null){
  956. document.ondrag = c._oldHandlers.ondrag;
  957. c._oldHandlers.ondrag = null;
  958. }
  959. if (document.onmousedown != undefined && c._oldHandlers.onmousedown != null){
  960. document.onmousedown = c._oldHandlers.onmousedown;
  961. c._oldHandlers.onmousedown = null;
  962. }
  963. }
  964. function drawZoomBox() {
  965. var start = this._zoom.start;
  966. var end = this._zoom.end;
  967. var ctx = this.zoomCanvas._ctx;
  968. var l, t, h, w;
  969. if (end[0] > start[0]) {
  970. l = start[0];
  971. w = end[0] - start[0];
  972. }
  973. else {
  974. l = end[0];
  975. w = start[0] - end[0];
  976. }
  977. if (end[1] > start[1]) {
  978. t = start[1];
  979. h = end[1] - start[1];
  980. }
  981. else {
  982. t = end[1];
  983. h = start[1] - end[1];
  984. }
  985. ctx.fillStyle = 'rgba(0,0,0,0.2)';
  986. ctx.strokeStyle = '#999999';
  987. ctx.lineWidth = 1.0;
  988. ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
  989. ctx.fillRect(0,0,ctx.canvas.width, ctx.canvas.height);
  990. ctx.clearRect(l, t, w, h);
  991. // IE won't show transparent fill rect, so stroke a rect also.
  992. ctx.strokeRect(l,t,w,h);
  993. ctx = null;
  994. }
  995. $.jqplot.CursorLegendRenderer = function(options) {
  996. $.jqplot.TableLegendRenderer.call(this, options);
  997. this.formatString = '%s';
  998. };
  999. $.jqplot.CursorLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
  1000. $.jqplot.CursorLegendRenderer.prototype.constructor = $.jqplot.CursorLegendRenderer;
  1001. // called in context of a Legend
  1002. $.jqplot.CursorLegendRenderer.prototype.draw = function() {
  1003. if (this._elem) {
  1004. this._elem.emptyForce();
  1005. this._elem = null;
  1006. }
  1007. if (this.show) {
  1008. var series = this._series, s;
  1009. // make a table. one line label per row.
  1010. var elem = document.createElement('table');
  1011. this._elem = $(elem);
  1012. elem = null;
  1013. this._elem.addClass('jqplot-legend jqplot-cursor-legend');
  1014. this._elem.css('position', 'absolute');
  1015. var pad = false;
  1016. for (var i = 0; i< series.length; i++) {
  1017. s = series[i];
  1018. if (s.show && s.showLabel) {
  1019. var lt = $.jqplot.sprintf(this.formatString, s.label.toString());
  1020. if (lt) {
  1021. var color = s.color;
  1022. if (s._stack && !s.fill) {
  1023. color = '';
  1024. }
  1025. addrow.call(this, lt, color, pad, i);
  1026. pad = true;
  1027. }
  1028. // let plugins add more rows to legend. Used by trend line plugin.
  1029. for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) {
  1030. var item = $.jqplot.addLegendRowHooks[j].call(this, s);
  1031. if (item) {
  1032. addrow.call(this, item.label, item.color, pad);
  1033. pad = true;
  1034. }
  1035. }
  1036. }
  1037. }
  1038. series = s = null;
  1039. delete series;
  1040. delete s;
  1041. }
  1042. function addrow(label, color, pad, idx) {
  1043. var rs = (pad) ? this.rowSpacing : '0';
  1044. var tr = $('<tr class="jqplot-legend jqplot-cursor-legend"></tr>').appendTo(this._elem);
  1045. tr.data('seriesIndex', idx);
  1046. $('<td class="jqplot-legend jqplot-cursor-legend-swatch" style="padding-top:'+rs+';">'+
  1047. '<div style="border:1px solid #cccccc;padding:0.2em;">'+
  1048. '<div class="jqplot-cursor-legend-swatch" style="background-color:'+color+';"></div>'+
  1049. '</div></td>').appendTo(tr);
  1050. var td = $('<td class="jqplot-legend jqplot-cursor-legend-label" style="vertical-align:middle;padding-top:'+rs+';"></td>');
  1051. td.appendTo(tr);
  1052. td.data('seriesIndex', idx);
  1053. if (this.escapeHtml) {
  1054. td.text(label);
  1055. }
  1056. else {
  1057. td.html(label);
  1058. }
  1059. tr = null;
  1060. td = null;
  1061. }
  1062. return this._elem;
  1063. };
  1064. })(jQuery);