Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

421 rader
19 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. $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]);
  32. /**
  33. * Class: $.jqplot.Highlighter
  34. * Plugin which will highlight data points when they are moused over.
  35. *
  36. * To use this plugin, include the js
  37. * file in your source:
  38. *
  39. * > <script type="text/javascript" src="plugins/jqplot.highlighter.js"></script>
  40. *
  41. * A tooltip providing information about the data point is enabled by default.
  42. * To disable the tooltip, set "showTooltip" to false.
  43. *
  44. * You can control what data is displayed in the tooltip with various
  45. * options. The "tooltipAxes" option controls wether the x, y or both
  46. * data values are displayed.
  47. *
  48. * Some chart types (e.g. hi-low-close) have more than one y value per
  49. * data point. To display the additional values in the tooltip, set the
  50. * "yvalues" option to the desired number of y values present (3 for a hlc chart).
  51. *
  52. * By default, data values will be formatted with the same formatting
  53. * specifiers as used to format the axis ticks. A custom format code
  54. * can be supplied with the tooltipFormatString option. This will apply
  55. * to all values in the tooltip.
  56. *
  57. * For more complete control, the "formatString" option can be set. This
  58. * Allows conplete control over tooltip formatting. Values are passed to
  59. * the format string in an order determined by the "tooltipAxes" and "yvalues"
  60. * options. So, if you have a hi-low-close chart and you just want to display
  61. * the hi-low-close values in the tooltip, you could set a formatString like:
  62. *
  63. * > highlighter: {
  64. * > tooltipAxes: 'y',
  65. * > yvalues: 3,
  66. * > formatString:'<table class="jqplot-highlighter">
  67. * > <tr><td>hi:</td><td>%s</td></tr>
  68. * > <tr><td>low:</td><td>%s</td></tr>
  69. * > <tr><td>close:</td><td>%s</td></tr></table>'
  70. * > }
  71. *
  72. */
  73. $.jqplot.Highlighter = function(options) {
  74. // Group: Properties
  75. //
  76. //prop: show
  77. // true to show the highlight.
  78. this.show = $.jqplot.config.enablePlugins;
  79. // prop: markerRenderer
  80. // Renderer used to draw the marker of the highlighted point.
  81. // Renderer will assimilate attributes from the data point being highlighted,
  82. // so no attributes need set on the renderer directly.
  83. // Default is to turn off shadow drawing on the highlighted point.
  84. this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false});
  85. // prop: showMarker
  86. // true to show the marker
  87. this.showMarker = true;
  88. // prop: lineWidthAdjust
  89. // Pixels to add to the lineWidth of the highlight.
  90. this.lineWidthAdjust = 2.5;
  91. // prop: sizeAdjust
  92. // Pixels to add to the overall size of the highlight.
  93. this.sizeAdjust = 5;
  94. // prop: showTooltip
  95. // Show a tooltip with data point values.
  96. this.showTooltip = true;
  97. // prop: tooltipLocation
  98. // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
  99. this.tooltipLocation = 'nw';
  100. // prop: fadeTooltip
  101. // true = fade in/out tooltip, flase = show/hide tooltip
  102. this.fadeTooltip = true;
  103. // prop: tooltipFadeSpeed
  104. // 'slow', 'def', 'fast', or number of milliseconds.
  105. this.tooltipFadeSpeed = "fast";
  106. // prop: tooltipOffset
  107. // Pixel offset of tooltip from the highlight.
  108. this.tooltipOffset = 2;
  109. // prop: tooltipAxes
  110. // Which axes to display in tooltip, 'x', 'y' or 'both', 'xy' or 'yx'
  111. // 'both' and 'xy' are equivalent, 'yx' reverses order of labels.
  112. this.tooltipAxes = 'both';
  113. // prop; tooltipSeparator
  114. // String to use to separate x and y axes in tooltip.
  115. this.tooltipSeparator = ', ';
  116. // prop; tooltipContentEditor
  117. // Function used to edit/augment/replace the formatted tooltip contents.
  118. // Called as str = tooltipContentEditor(str, seriesIndex, pointIndex)
  119. // where str is the generated tooltip html and seriesIndex and pointIndex identify
  120. // the data point being highlighted. Should return the html for the tooltip contents.
  121. this.tooltipContentEditor = null;
  122. // prop: useAxesFormatters
  123. // Use the x and y axes formatters to format the text in the tooltip.
  124. this.useAxesFormatters = true;
  125. // prop: tooltipFormatString
  126. // sprintf format string for the tooltip.
  127. // Uses Ash Searle's javascript sprintf implementation
  128. // found here: http://hexmen.com/blog/2007/03/printf-sprintf/
  129. // See http://perldoc.perl.org/functions/sprintf.html for reference.
  130. // Additional "p" and "P" format specifiers added by Chris Leonello.
  131. this.tooltipFormatString = '%.5P';
  132. // prop: formatString
  133. // alternative to tooltipFormatString
  134. // will format the whole tooltip text, populating with x, y values as
  135. // indicated by tooltipAxes option. So, you could have a tooltip like:
  136. // 'Date: %s, number of cats: %d' to format the whole tooltip at one go.
  137. // If useAxesFormatters is true, values will be formatted according to
  138. // Axes formatters and you can populate your tooltip string with
  139. // %s placeholders.
  140. this.formatString = null;
  141. // prop: yvalues
  142. // Number of y values to expect in the data point array.
  143. // Typically this is 1. Certain plots, like OHLC, will
  144. // have more y values in each data point array.
  145. this.yvalues = 1;
  146. // prop: bringSeriesToFront
  147. // This option requires jQuery 1.4+
  148. // True to bring the series of the highlighted point to the front
  149. // of other series.
  150. this.bringSeriesToFront = false;
  151. this._tooltipElem;
  152. this.isHighlighting = false;
  153. $.extend(true, this, options);
  154. };
  155. var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];
  156. var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7};
  157. var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e'];
  158. // axis.renderer.tickrenderer.formatter
  159. // called with scope of plot
  160. $.jqplot.Highlighter.init = function (target, data, opts){
  161. var options = opts || {};
  162. // add a highlighter attribute to the plot
  163. this.plugins.highlighter = new $.jqplot.Highlighter(options.highlighter);
  164. };
  165. // called within scope of series
  166. $.jqplot.Highlighter.parseOptions = function (defaults, options) {
  167. // Add a showHighlight option to the series
  168. // and set it to true by default.
  169. this.showHighlight = true;
  170. };
  171. // called within context of plot
  172. // create a canvas which we can draw on.
  173. // insert it before the eventCanvas, so eventCanvas will still capture events.
  174. $.jqplot.Highlighter.postPlotDraw = function() {
  175. // Memory Leaks patch
  176. if (this.plugins.highlighter && this.plugins.highlighter.highlightCanvas) {
  177. this.plugins.highlighter.highlightCanvas.resetCanvas();
  178. this.plugins.highlighter.highlightCanvas = null;
  179. }
  180. if (this.plugins.highlighter && this.plugins.highlighter._tooltipElem) {
  181. this.plugins.highlighter._tooltipElem.emptyForce();
  182. this.plugins.highlighter._tooltipElem = null;
  183. }
  184. this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas();
  185. this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions, this));
  186. this.plugins.highlighter.highlightCanvas.setContext();
  187. var elem = document.createElement('div');
  188. this.plugins.highlighter._tooltipElem = $(elem);
  189. elem = null;
  190. this.plugins.highlighter._tooltipElem.addClass('jqplot-highlighter-tooltip');
  191. this.plugins.highlighter._tooltipElem.css({position:'absolute', display:'none'});
  192. this.eventCanvas._elem.before(this.plugins.highlighter._tooltipElem);
  193. };
  194. $.jqplot.preInitHooks.push($.jqplot.Highlighter.init);
  195. $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions);
  196. $.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw);
  197. function draw(plot, neighbor) {
  198. var hl = plot.plugins.highlighter;
  199. var s = plot.series[neighbor.seriesIndex];
  200. var smr = s.markerRenderer;
  201. var mr = hl.markerRenderer;
  202. mr.style = smr.style;
  203. mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust;
  204. mr.size = smr.size + hl.sizeAdjust;
  205. var rgba = $.jqplot.getColorComponents(smr.color);
  206. var newrgb = [rgba[0], rgba[1], rgba[2]];
  207. var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]);
  208. mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')';
  209. mr.init();
  210. mr.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], hl.highlightCanvas._ctx);
  211. }
  212. function showTooltip(plot, series, neighbor) {
  213. // neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}
  214. // gridData should be x,y pixel coords on the grid.
  215. // add the plot._gridPadding to that to get x,y in the target.
  216. var hl = plot.plugins.highlighter;
  217. var elem = hl._tooltipElem;
  218. if (hl.useAxesFormatters) {
  219. var xf = series._xaxis._ticks[0].formatter;
  220. var yf = series._yaxis._ticks[0].formatter;
  221. var xfstr = series._xaxis._ticks[0].formatString;
  222. var yfstr = series._yaxis._ticks[0].formatString;
  223. var str;
  224. var xstr = xf(xfstr, neighbor.data[0]);
  225. var ystrs = [];
  226. for (var i=1; i<hl.yvalues+1; i++) {
  227. ystrs.push(yf(yfstr, neighbor.data[i]));
  228. }
  229. if (hl.formatString) {
  230. switch (hl.tooltipAxes) {
  231. case 'both':
  232. case 'xy':
  233. ystrs.unshift(xstr);
  234. ystrs.unshift(hl.formatString);
  235. str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
  236. break;
  237. case 'yx':
  238. ystrs.push(xstr);
  239. ystrs.unshift(hl.formatString);
  240. str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
  241. break;
  242. case 'x':
  243. str = $.jqplot.sprintf.apply($.jqplot.sprintf, [hl.formatString, xstr]);
  244. break;
  245. case 'y':
  246. ystrs.unshift(hl.formatString);
  247. str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
  248. break;
  249. default: // same as xy
  250. ystrs.unshift(xstr);
  251. ystrs.unshift(hl.formatString);
  252. str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
  253. break;
  254. }
  255. }
  256. else {
  257. switch (hl.tooltipAxes) {
  258. case 'both':
  259. case 'xy':
  260. str = xstr;
  261. for (var i=0; i<ystrs.length; i++) {
  262. str += hl.tooltipSeparator + ystrs[i];
  263. }
  264. break;
  265. case 'yx':
  266. str = '';
  267. for (var i=0; i<ystrs.length; i++) {
  268. str += ystrs[i] + hl.tooltipSeparator;
  269. }
  270. str += xstr;
  271. break;
  272. case 'x':
  273. str = xstr;
  274. break;
  275. case 'y':
  276. str = ystrs.join(hl.tooltipSeparator);
  277. break;
  278. default: // same as 'xy'
  279. str = xstr;
  280. for (var i=0; i<ystrs.length; i++) {
  281. str += hl.tooltipSeparator + ystrs[i];
  282. }
  283. break;
  284. }
  285. }
  286. }
  287. else {
  288. var str;
  289. if (hl.tooltipAxes == 'both' || hl.tooltipAxes == 'xy') {
  290. str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[0]) + hl.tooltipSeparator + $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[1]);
  291. }
  292. else if (hl.tooltipAxes == 'yx') {
  293. str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[1]) + hl.tooltipSeparator + $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[0]);
  294. }
  295. else if (hl.tooltipAxes == 'x') {
  296. str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[0]);
  297. }
  298. else if (hl.tooltipAxes == 'y') {
  299. str = $.jqplot.sprintf(hl.tooltipFormatString, neighbor.data[1]);
  300. }
  301. }
  302. if ($.isFunction(hl.tooltipContentEditor)) {
  303. // args str, seriesIndex, pointIndex are essential so the hook can look up
  304. // extra data for the point.
  305. str = hl.tooltipContentEditor(str, neighbor.seriesIndex, neighbor.pointIndex, plot);
  306. }
  307. elem.html(str);
  308. var gridpos = {x:neighbor.gridData[0], y:neighbor.gridData[1]};
  309. var ms = 0;
  310. var fact = 0.707;
  311. if (series.markerRenderer.show == true) {
  312. ms = (series.markerRenderer.size + hl.sizeAdjust)/2;
  313. }
  314. var loc = locations;
  315. if (series.fillToZero && series.fill && neighbor.data[1] < 0) {
  316. loc = oppositeLocations;
  317. }
  318. switch (loc[locationIndicies[hl.tooltipLocation]]) {
  319. case 'nw':
  320. var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - fact * ms;
  321. var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - fact * ms;
  322. break;
  323. case 'n':
  324. var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
  325. var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - ms;
  326. break;
  327. case 'ne':
  328. var x = gridpos.x + plot._gridPadding.left + hl.tooltipOffset + fact * ms;
  329. var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - fact * ms;
  330. break;
  331. case 'e':
  332. var x = gridpos.x + plot._gridPadding.left + hl.tooltipOffset + ms;
  333. var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
  334. break;
  335. case 'se':
  336. var x = gridpos.x + plot._gridPadding.left + hl.tooltipOffset + fact * ms;
  337. var y = gridpos.y + plot._gridPadding.top + hl.tooltipOffset + fact * ms;
  338. break;
  339. case 's':
  340. var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
  341. var y = gridpos.y + plot._gridPadding.top + hl.tooltipOffset + ms;
  342. break;
  343. case 'sw':
  344. var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - fact * ms;
  345. var y = gridpos.y + plot._gridPadding.top + hl.tooltipOffset + fact * ms;
  346. break;
  347. case 'w':
  348. var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - ms;
  349. var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
  350. break;
  351. default: // same as 'nw'
  352. var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - hl.tooltipOffset - fact * ms;
  353. var y = gridpos.y + plot._gridPadding.top - hl.tooltipOffset - elem.outerHeight(true) - fact * ms;
  354. break;
  355. }
  356. elem.css('left', x);
  357. elem.css('top', y);
  358. if (hl.fadeTooltip) {
  359. // Fix for stacked up animations. Thnanks Trevor!
  360. elem.stop(true,true).fadeIn(hl.tooltipFadeSpeed);
  361. }
  362. else {
  363. elem.show();
  364. }
  365. elem = null;
  366. }
  367. function handleMove(ev, gridpos, datapos, neighbor, plot) {
  368. var hl = plot.plugins.highlighter;
  369. var c = plot.plugins.cursor;
  370. if (hl.show) {
  371. if (neighbor == null && hl.isHighlighting) {
  372. var ctx = hl.highlightCanvas._ctx;
  373. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  374. if (hl.fadeTooltip) {
  375. hl._tooltipElem.fadeOut(hl.tooltipFadeSpeed);
  376. }
  377. else {
  378. hl._tooltipElem.hide();
  379. }
  380. if (hl.bringSeriesToFront) {
  381. plot.restorePreviousSeriesOrder();
  382. }
  383. hl.isHighlighting = false;
  384. ctx = null;
  385. }
  386. else if (neighbor != null && plot.series[neighbor.seriesIndex].showHighlight && !hl.isHighlighting) {
  387. hl.isHighlighting = true;
  388. if (hl.showMarker) {
  389. draw(plot, neighbor);
  390. }
  391. if (hl.showTooltip && (!c || !c._zoom.started)) {
  392. showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor);
  393. }
  394. if (hl.bringSeriesToFront) {
  395. plot.moveSeriesToFront(neighbor.seriesIndex);
  396. }
  397. }
  398. }
  399. }
  400. })(jQuery);